diff --git a/.github/workflows/hook-tests.yaml b/.github/workflows/hook-tests.yaml index 9be2120e5..2e6bb9cd9 100644 --- a/.github/workflows/hook-tests.yaml +++ b/.github/workflows/hook-tests.yaml @@ -61,7 +61,7 @@ jobs: options(install.packages.compile.from.source = "never", pkgType = "binary") renv::restore() # install hook-specific additional_dependencies from .pre-commit-config.yaml - renv::install(c('pkgdown', 'mockery')) + renv::install(c('pkgdown')) renv::install(getwd(), dependencies = FALSE) # needed to make sure renv is activated in run_test() activate <- c( @@ -97,12 +97,15 @@ jobs: - name: Test run: | pkgload::load_all() - testthat::test_file( - "tests/testthat/test-hooks.R", - reporter = testthat::MultiReporter$new(list( - testthat::CheckReporter$new(), testthat::FailReporter$new() - )) - ) + hook_test_files <- as.character(fs::dir_ls('tests/testthat/', regexp = "test-hook-.*\\.R", type = "file")) + tester <- function(file) { + testthat::test_file(file, + reporter = testthat::MultiReporter$new(list( + testthat::CheckReporter$new(), testthat::FailReporter$new() + )) + ) + } + purrr::walk(hook_test_files, tester) shell: Rscript {0} - name: Show testthat output if: always() diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3d7d443d1..9f76da94f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -106,7 +106,7 @@ repos: # Only required when https://pre-commit.ci is used for config validation - id: check-pre-commit-ci-config - repo: https://github.com/lorenzwalthert/gitignore-tidy - rev: 475bf5d96927a1887ce2863ff3075b1d7240bc51 + rev: 0.1.2 hooks: - id: tidy-gitignore - repo: local diff --git a/DESCRIPTION b/DESCRIPTION index 6b1bbe89c..7d0f3871d 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: precommit Title: Pre-Commit Hooks -Version: 0.4.0.9000 +Version: 0.4.1 Author: Lorenz Walthert Maintainer: Lorenz Walthert Description: Useful git hooks for R building on top of the multi-language @@ -29,7 +29,6 @@ Suggests: glue, knitr, lintr, - mockery, pkgload, pkgdown, reticulate (>= 1.16), @@ -50,3 +49,5 @@ Roxygen: list(markdown = TRUE, roclets = c( "rd", "namespace", "collate", up to date"); NULL} ) ) RoxygenNote: 7.3.1 SystemRequirements: git +Config/testthat/parallel: true +Config/testthat/edition: 3 diff --git a/NEWS.md b/NEWS.md index 12f223896..d5d262716 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,13 @@ +# precommit 0.4.1 + +This release ensures unit tests can handle the error messages from {styler} +correctly that were modified slightly. Apart from hook dependency updates, this +release adds no user-facing changes. + +Thanks [@joshpersi](https://github.com/joshpersi) for contributing to this +release. + + # precommit 0.4.0 diff --git a/R/config.R b/R/config.R index 59ae0a213..98e5a98b1 100644 --- a/R/config.R +++ b/R/config.R @@ -84,7 +84,7 @@ set_config_source <- function(config_source, target <- fs::path_ext_set(tmp, fs::path_ext(config_source)) utils::download.file(config_source, target, quiet = TRUE) - rlang::with_handlers( + rlang::try_fetch( yaml::read_yaml(target, fileEncoding = "UTF-8"), error = function(e) { rlang::abort(paste0( diff --git a/R/exec.R b/R/exec.R index 5d15047ee..698a85448 100644 --- a/R/exec.R +++ b/R/exec.R @@ -32,6 +32,15 @@ path_precommit_exec <- function(check_if_exists = TRUE) { final } +#' Get the operating System +#' +#' Can't mock base package (either because it's an `.Internal` or for some other +#' reason). +#' @keywords internal +get_os <- function() { + tolower(Sys.info()[["sysname"]]) +} + #' @rdname path_precommit_exec #' @examples #' \dontrun{ @@ -56,7 +65,7 @@ path_pre_commit_exec <- function(check_if_exists = TRUE) { #' @keywords internal path_derive_precommit_exec <- function() { path <- path_derive_precommit_exec_path() - os <- tolower(Sys.info()[["sysname"]]) + os <- get_os() if (os == "darwin") { path <- c(path, path_derive_precommit_exec_macOS()) } else if (os == "windows") { diff --git a/R/release.R b/R/release.R index d58164cbc..427b4946f 100644 --- a/R/release.R +++ b/R/release.R @@ -139,8 +139,14 @@ release_prechecks <- function(bump, is_cran) { dsc <- desc::description$new() suppressMessages(dsc$bump_version(bump)) new_version <- paste0("v", dsc$get_version()) + if (is_cran) { + release_branch <- paste0("rc-", new_version) + if (!(release_branch %in% names(git2r::branches()))) { + rlang::abort(paste0("need to be on branch ", release_branch)) + } + } abort_if_not_yes("Your target release has version {new_version}, correct?") - abort_if_not_yes("Did you prepare NEWS.md for this version ({new_version})?") + abort_if_not_yes("Did you commit NEWS.md for this version ({new_version})?") dsc } diff --git a/R/roxygen2.R b/R/roxygen2.R index 1f839c193..b31244f80 100644 --- a/R/roxygen2.R +++ b/R/roxygen2.R @@ -39,7 +39,7 @@ diff_requires_run_roxygenize <- function(root = ".") { #' @keywords internal #' @export roxygen_assert_additional_dependencies <- function() { - out <- rlang::with_handlers( + out <- rlang::try_fetch( # roxygen2 will load: https://github.com/r-lib/roxygen2/issues/771 pkgload::load_all(quiet = TRUE), error = function(e) { @@ -80,7 +80,7 @@ roxygen_assert_additional_dependencies <- function() { #' @importFrom R.cache saveCache # fails if accessed with R.cache::saveCache()! roxygenize_with_cache <- function(key, dirs) { - out <- rlang::with_handlers( + out <- rlang::try_fetch( roxygen2::roxygenise(), error = function(e) e ) diff --git a/R/setup.R b/R/setup.R index 8bdfb10fd..80b7779d6 100644 --- a/R/setup.R +++ b/R/setup.R @@ -167,7 +167,7 @@ autoupdate <- function(root = here::here()) { ensure_renv_precommit_compat <- function(package_version_renv = utils::packageVersion("renv"), root = here::here()) { - is_precommit <- suppressWarnings(rlang::with_handlers( + is_precommit <- suppressWarnings(rlang::try_fetch( unname(read.dcf("DESCRIPTION")[, "Package"]) == "precommit", error = function(e) FALSE )) @@ -189,7 +189,7 @@ ensure_renv_precommit_compat <- function(package_version_renv = utils::packageVe } rev <- rev_read(path_config) - should_fail <- rlang::with_handlers( + should_fail <- rlang::try_fetch( { rev <- rev_as_pkg_version(rev) maximal_rev <- package_version("0.1.3.9014") @@ -279,7 +279,7 @@ snippet_generate <- function(snippet = "", "supported for {.url pre-commit.ci}. See ", '{.code vignette("ci", package = "precommit")} for details and solutions.' )) - remote_deps <- rlang::with_handlers( + remote_deps <- rlang::try_fetch( desc::desc_get_field("Remotes"), error = function(e) character() ) diff --git a/R/testing.R b/R/testing.R index 2e0f17913..1f593169c 100644 --- a/R/testing.R +++ b/R/testing.R @@ -227,7 +227,7 @@ hook_state_assert_one <- function(path_candidate, if (exit_status != 0) { testthat::fail("Expected: No error. Found:", contents) } - testthat::expect_equivalent(candidate, reference) + testthat::expect_equal(candidate, reference, ignore_attr = TRUE) if (!is.null(std_out)) { contents <- readLines(path_stdout) testthat::expect_match( diff --git a/R/utils.R b/R/utils.R index 507736f08..f62e50880 100644 --- a/R/utils.R +++ b/R/utils.R @@ -30,7 +30,7 @@ is_conda_installation <- function() { } is_package <- function(root = here::here()) { - rlang::with_handlers( + rlang::try_fetch( rprojroot::find_package_root_file(path = root), error = function(e) NULL ) %>% @@ -129,7 +129,7 @@ has_git <- function() { is_git_repo <- function(root = here::here()) { withr::local_dir(root) if (has_git()) { - rlang::with_handlers( + rlang::try_fetch( { output <- call_and_capture( "git", diff --git a/cran-comments.md b/cran-comments.md index 727d0c315..57ffbcb97 100644 --- a/cran-comments.md +++ b/cran-comments.md @@ -1,11 +1,10 @@ -This is a submission due to changes in roxygen2 7.3.0 and fixes also some issues -related to R devel changes. +This is a submission due to changes in styler's error messages. ## Test environments -- ubuntu 18.04 (on GitHub Actions): R 4.2 -- Windows (on GitHub Actions): R 4.2 -- macOS (on GitHub Actions): R 4.2 +- ubuntu 18.04 (on GitHub Actions): R 4.3 +- Windows (on GitHub Actions): R 4.3 +- macOS (on GitHub Actions): R 4.3 - win-builder: R devel ## R CMD check results diff --git a/inst/WORDLIST b/inst/WORDLIST index 81d4e4d3d..2817f96f9 100644 --- a/inst/WORDLIST +++ b/inst/WORDLIST @@ -192,6 +192,7 @@ stdout sterr stopifnot styler +styler's sublicenses Sublicensing Sys diff --git a/inst/pre-commit-config-pkg.yaml b/inst/pre-commit-config-pkg.yaml index 5b8134e56..4bceb5a97 100644 --- a/inst/pre-commit-config-pkg.yaml +++ b/inst/pre-commit-config-pkg.yaml @@ -2,7 +2,7 @@ # R specific hooks: https://github.com/lorenzwalthert/precommit repos: - repo: https://github.com/lorenzwalthert/precommit - rev: v0.4.0 + rev: v0.4.1 hooks: - id: style-files args: [--style_pkg=styler, --style_fun=tidyverse_style] diff --git a/inst/pre-commit-config-proj.yaml b/inst/pre-commit-config-proj.yaml index 912196091..dcf7be251 100644 --- a/inst/pre-commit-config-proj.yaml +++ b/inst/pre-commit-config-proj.yaml @@ -2,7 +2,7 @@ # R specific hooks: https://github.com/lorenzwalthert/precommit repos: - repo: https://github.com/lorenzwalthert/precommit - rev: v0.4.0 + rev: v0.4.1 hooks: - id: style-files args: [--style_pkg=styler, --style_fun=tidyverse_style] diff --git a/inst/update-dependency-graph-existing-packages.R b/inst/update-dependency-graph-existing-packages.R index d63781cab..a18bc8e63 100644 --- a/inst/update-dependency-graph-existing-packages.R +++ b/inst/update-dependency-graph-existing-packages.R @@ -4,10 +4,10 @@ hook_deps <- function(root) { deps <- desc$get_deps() dont <- c( "yaml", "usethis", "withr", "rstudioapi", "precommit", - "pkgdown", "mockery", + "pkgdown", "httr" ) - out <- c(out, "docopt", "roxygen2", "spelling", "styler", "pkgload", "lintr", "knitr", "desc", "mockery") + out <- c(out, "docopt", "roxygen2", "spelling", "styler", "pkgload", "lintr", "knitr", "desc") out <- setdiff(c(unique(c(out, deps[deps$type == "Imports", ]$package))), dont) out <- names(renv:::renv_package_dependencies(out)) return(sort(out)) diff --git a/man/get_os.Rd b/man/get_os.Rd new file mode 100644 index 000000000..1ed4eca01 --- /dev/null +++ b/man/get_os.Rd @@ -0,0 +1,13 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/exec.R +\name{get_os} +\alias{get_os} +\title{Get the operating System} +\usage{ +get_os() +} +\description{ +Can't mock base package (either because it's an \code{.Internal} or for some other +reason). +} +\keyword{internal} diff --git a/tests/testthat/test-conda.R b/tests/testthat/test-conda.R index 83bee26af..c4a7f1e42 100644 --- a/tests/testthat/test-conda.R +++ b/tests/testthat/test-conda.R @@ -181,8 +181,9 @@ if (!on_cran()) { }) test_that("can update via conda", { if (not_conda()) { + local_mocked_bindings(assert_reticulate_is_installed = function(...) NULL) expect_error( - with_mock(update_precommit(), "precommit:::assert_reticulate_is_installed" = function(...) NULL), + update_precommit(), paste( "You can only update your pre-commit executable via the R API if you", "chose the installation method via conda" @@ -201,3 +202,39 @@ if (!on_cran()) { } }) } + +test_that("Autoupdate is not conducted when renv present in incompatible setup", { + skip_on_cran() + + # mock old pre-commit and renv versions + local_mocked_bindings(version_precommit = function(...) "2.13.0") + + local_test_setup( + git = TRUE, use_precommit = TRUE, install_hooks = FALSE, open = FALSE + ) + initial <- rev_read() %>% + rev_as_pkg_version() + # simulate adding {renv} + writeLines("", "renv.lock") + + # should downgrade rev + expect_error( + ensure_renv_precommit_compat( + package_version_renv = package_version("0.13.0"), root = getwd() + ), + "Please update" + ) + downgraded <- rev_read() %>% + rev_as_pkg_version() + expect_true(downgraded == initial) + + # simulate removing {renv} should be updated + fs::file_delete("renv.lock") + expect_warning( + ensure_renv_precommit_compat( + package_version("0.13.0"), + root = getwd() + ), + NA + ) +}) diff --git a/tests/testthat/test-exec.R b/tests/testthat/test-exec.R index dcb567c24..69cace352 100644 --- a/tests/testthat/test-exec.R +++ b/tests/testthat/test-exec.R @@ -1,21 +1,25 @@ -test_that("Path can be derived for windows Python >= 3.0", { - paths_base <- with_mock( - "precommit::path_derive_precommit_exec_win_python3plus_candidates" = function() { +test_that("Path can be derived for windows Python >= 3.0 (mocked)", { + local_mocked_bindings( + path_derive_precommit_exec_win_python3plus_candidates = function() { c( fs::path_home("AppData/Roaming/Python/Python35"), fs::path_home("AppData/Roaming/Python/Python37") ) - }, - path_derive_precommit_exec_win_python3plus_base() + } ) expect_equal( - paths_base, + path_derive_precommit_exec_win_python3plus_base(), c( fs::path(fs::path_home(), "AppData/Roaming/Python/Python37/Scripts"), fs::path(fs::path_home(), "AppData/Roaming/Python/Python35/Scripts") - ) + ), + ignore_attr = TRUE ) +}) + + +test_that("Path can be derived for windows Python >= 3.0 (actual)", { skip_if(!is_windows()) skip_if(!not_conda()) skip_if(on_cran()) @@ -28,61 +32,63 @@ test_that("Path can be derived for windows Python >= 3.0", { test_that("Warns when there are multiple installations found (2x os)", { + local_mocked_bindings( + path_derive_precommit_exec_path = function(candidate) { + fs::path_home("AppData/Roaming/Python/Python35") + }, + get_os = function(...) { + c(sysname = "windows") + }, + path_derive_precommit_exec_win = function() { + c( + fs::path_home("AppData/Roaming/Python/Python34"), + fs::path_home("AppData/Roaming/Python/Python37") + ) + } + ) + expect_warning( - with_mock( - "precommit::path_derive_precommit_exec_path" = function(candidate) { - fs::path_home("AppData/Roaming/Python/Python35") - }, - "Sys.info" = function(...) { - c(sysname = "windows") - }, - "precommit:::path_derive_precommit_exec_win" = function() { - c( - fs::path_home("AppData/Roaming/Python/Python34"), - fs::path_home("AppData/Roaming/Python/Python37") - ) - }, - path_derive_precommit_exec() - ), + path_derive_precommit_exec(), "We detected multiple pre-commit executables" ) }) test_that("Warns when there are multiple installations found (2x path)", { + local_mocked_bindings( + path_derive_precommit_exec_path = function(candidate) { + c( + fs::path_home("AppData/Roaming/Python/Python35"), + fs::path_home("AppData/Roaming/Python/Python37") + ) + }, + get_os = function(...) { + c(sysname = "windows") + }, + path_derive_precommit_exec_win = function() { + fs::path_home("AppData/Roaming/Python/Python34") + } + ) expect_warning( - with_mock( - "precommit::path_derive_precommit_exec_path" = function(candidate) { - c( - fs::path_home("AppData/Roaming/Python/Python35"), - fs::path_home("AppData/Roaming/Python/Python37") - ) - }, - "Sys.info" = function(...) { - c(sysname = "windows") - }, - "precommit:::path_derive_precommit_exec_win" = function() { - fs::path_home("AppData/Roaming/Python/Python34") - }, - path_derive_precommit_exec() - ), + path_derive_precommit_exec(), "We detected multiple pre-commit executables" ) }) test_that("Warns when there are multiple installations found (path and os)", { + local_mocked_bindings( + path_derive_precommit_exec_path = function(candidate) { + fs::path_home("AppData/Roaming/Python/Python35") + }, + path_derive_precommit_exec_win = function() { + fs::path_home("AppData/Roaming/Python/Python34") + }, + get_os = function(...) { + c(sysname = "windows") + }, + ) + expect_warning( - with_mock( - "precommit::path_derive_precommit_exec_path" = function(candidate) { - fs::path_home("AppData/Roaming/Python/Python35") - }, - "Sys.info" = function(...) { - c(sysname = "windows") - }, - "precommit:::path_derive_precommit_exec_win" = function() { - fs::path_home("AppData/Roaming/Python/Python34") - }, - path_derive_precommit_exec() - ), + path_derive_precommit_exec(), "We detected multiple pre-commit executables" ) }) diff --git a/tests/testthat/test-hook-codemeta-description-updated.R b/tests/testthat/test-hook-codemeta-description-updated.R new file mode 100644 index 000000000..0ec6e7949 --- /dev/null +++ b/tests/testthat/test-hook-codemeta-description-updated.R @@ -0,0 +1,88 @@ +run_test("codemeta-description-update", + file_name = c("codemeta.json"), + suffix = "", + std_err = "No `DESCRIPTION` found in repository.", + std_out = NULL, +) + +run_test("codemeta-description-update", + file_name = c("DESCRIPTION"), + suffix = "", + std_err = "No `codemeta.json` found in repository.", + std_out = NULL, +) + + +# outdated +run_test("codemeta-description-update", + file_name = c("DESCRIPTION", "codemeta.json"), + suffix = "", + std_err = "out of date", + std_out = NULL, + file_transformer = function(files) { + if (length(files) > 1) { + # transformer is called once on all files and once per file + content_2 <- readLines(files[1]) + Sys.sleep(2) + writeLines(content_2, files[1]) + } + files + } +) + +# succeed +run_test("codemeta-description-update", + file_name = c("DESCRIPTION", "codemeta.json"), + suffix = "", + file_transformer = function(files) { + if (length(files) > 1) { + # transformer is called once on all files and once per file + content_2 <- readLines(files[2]) + Sys.sleep(2) + writeLines(content_2, files[2]) + } + files + } +) + +if (!on_cran()) { + # succeed in correct root + run_test("codemeta-description-update", + file_name = c( + "rpkg/DESCRIPTION" = "DESCRIPTION", + "rpkg/codemeta.json" = "codemeta.json" + ), + cmd_args = "--root=rpkg", + suffix = "", + file_transformer = function(files) { + if (length(files) > 1) { + # transformer is called once on all files and once per file + content_2 <- readLines(files[2]) + Sys.sleep(2) + writeLines(content_2, files[2]) + } + files + } + ) + + # # fail in wrong root + run_test("codemeta-description-update", + file_name = c( + "rpkg/DESCRIPTION" = "DESCRIPTION", + "rpkg/codemeta.json" = "codemeta.json", + "rpkg2/codemeta.json" = "README.md" + ), + cmd_args = "--root=rpkg2", + std_err = "No `DESCRIPTION` found in repository.", + suffix = "", + file_transformer = function(files) { + if (length(files) > 1) { + # transformer is called once on all files and once per file + content_2 <- readLines(files[2]) + Sys.sleep(2) + writeLines(content_2, files[2]) + } + files + } + ) +} diff --git a/tests/testthat/test-hook-deps-in-desc-R.R b/tests/testthat/test-hook-deps-in-desc-R.R new file mode 100644 index 000000000..ff7ab8ddf --- /dev/null +++ b/tests/testthat/test-hook-deps-in-desc-R.R @@ -0,0 +1,133 @@ +# succeed (call to library that is in description) +run_test("deps-in-desc", + suffix = "-success.R", std_err = NULL, + artifacts = c("DESCRIPTION" = test_path("in/DESCRIPTION")) +) + +# fail (call to library that is not in description) +run_test("deps-in-desc", + suffix = "-fail.R", std_err = "Dependency check failed", + artifacts = c("DESCRIPTION" = test_path("in/DESCRIPTION")) +) + +# in sub directory with wrong root +run_test("deps-in-desc", + suffix = "-fail.R", std_err = "Could not find R package", + file_transformer = function(files) { + fs::path_abs(fs::file_move(files, "rpkg")) + }, + artifacts = c("rpkg/DESCRIPTION" = test_path("in/DESCRIPTION")) +) + +# in sub directory with correct root +if (!on_cran()) { + run_test("deps-in-desc", + cmd_args = "--root=rpkg", + suffix = "-fail.R", std_err = "Dependency check failed", + file_transformer = function(files) { + fs::path_abs(fs::file_move(files, "rpkg")) + }, + artifacts = c("rpkg/DESCRIPTION" = test_path("in/DESCRIPTION")) + ) + # in sub directory with correct root + run_test("deps-in-desc", + cmd_args = "--root=rpkg", + suffix = "-success.R", std_err = NULL, + file_transformer = function(files) { + fs::path_abs(fs::file_move(files, "rpkg")) + }, + artifacts = c("rpkg/DESCRIPTION" = test_path("in/DESCRIPTION")) + ) +} + + +# with ::: +run_test("deps-in-desc", + "deps-in-desc-dot3", + suffix = "-fail.R", std_err = "Dependency check failed", + artifacts = c("DESCRIPTION" = test_path("in/DESCRIPTION")) +) + +run_test("deps-in-desc", + "deps-in-desc-dot3", + suffix = "-success.R", std_err = NULL, + artifacts = c("DESCRIPTION" = test_path("in/DESCRIPTION")) +) + +run_test("deps-in-desc", + "deps-in-desc-dot3", + suffix = "-fail.R", std_err = NULL, + artifacts = c("DESCRIPTION" = test_path("in/DESCRIPTION")), + cmd_args = "--allow_private_imports" +) + +# Rmd +run_test("deps-in-desc", + "deps-in-desc", + suffix = "-fail.Rmd", std_err = "Dependency check failed", + std_out = "deps-in-desc-fail.Rmd`: ttyzp", + artifacts = c("DESCRIPTION" = test_path("in/DESCRIPTION")) +) + +run_test("deps-in-desc", + "deps-in-desc", + suffix = "-success.Rmd", std_err = NULL, + artifacts = c("DESCRIPTION" = test_path("in/DESCRIPTION")) +) + +# README.Rmd is excluded +run_test("deps-in-desc", + "README.Rmd", + suffix = "", std_err = NULL, + artifacts = c("DESCRIPTION" = test_path("in/DESCRIPTION-no-deps.dcf")) +) + + + +# Rnw +run_test("deps-in-desc", + "deps-in-desc", + suffix = "-fail.Rnw", std_err = "Dependency check failed", + artifacts = c("DESCRIPTION" = test_path("in/DESCRIPTION")) +) + +run_test("deps-in-desc", + "deps-in-desc", + suffix = "-success.Rnw", std_err = NULL, + artifacts = c("DESCRIPTION" = test_path("in/DESCRIPTION")) +) + +# Rprofile +# because .Rprofile is executed on startup, this must be an installed +# package (to not give an error staight away) not listed in +# test_path("in/DESCRIPTION") +if (Sys.getenv("GITHUB_WORKFLOW") != "Hook tests") { + # seems like .Rprofile with renv activation does not get executed when + # argument to Rscript contains Rprofile ?! Skip this + expect_true(rlang::is_installed("R.cache")) + run_test("deps-in-desc", + "Rprofile", + suffix = "", std_err = "Dependency check failed", + artifacts = c("DESCRIPTION" = test_path("in/DESCRIPTION")), + file_transformer = function(files) { + writeLines("R.cache::findCache", files) + fs::file_move( + files, + fs::path(fs::path_dir(files), paste0(".", fs::path_file(files))) + ) + } + ) + + run_test("deps-in-desc", + "Rprofile", + suffix = "", std_err = NULL, + artifacts = c("DESCRIPTION" = test_path("in/DESCRIPTION")), + file_transformer = function(files) { + writeLines("utils::head", files) + fs::file_move( + files, + fs::path(fs::path_dir(files), paste0(".", fs::path_file(files))) + ) + } + ) +} diff --git a/tests/testthat/test-hook-lintr.R b/tests/testthat/test-hook-lintr.R new file mode 100644 index 000000000..1a5574190 --- /dev/null +++ b/tests/testthat/test-hook-lintr.R @@ -0,0 +1,14 @@ +# success +run_test("lintr", + suffix = "-success.R", + std_err = NULL +) + +# failure +run_test("lintr", suffix = "-fail.R", std_err = "not lint free") + +# warning +run_test( + "lintr", + suffix = "-fail.R", cmd_args = "--warn_only", std_err = NULL +) diff --git a/tests/testthat/test-hook-no-browser-statement.R b/tests/testthat/test-hook-no-browser-statement.R new file mode 100644 index 000000000..56ff5a209 --- /dev/null +++ b/tests/testthat/test-hook-no-browser-statement.R @@ -0,0 +1,13 @@ +# success +run_test( + "no-browser-statement", + suffix = "-success.R", + std_err = NULL +) + +# failure +run_test( + "no-browser-statement", + suffix = "-fail.R", + std_err = "contains a `browser()` statement." +) diff --git a/tests/testthat/test-hook-no-debug-statement.R b/tests/testthat/test-hook-no-debug-statement.R new file mode 100644 index 000000000..45ac2eb14 --- /dev/null +++ b/tests/testthat/test-hook-no-debug-statement.R @@ -0,0 +1,13 @@ +# success +run_test( + "no-debug-statement", + suffix = "-success.R", + std_err = NULL +) + +# failure +run_test( + "no-debug-statement", + suffix = "-fail.R", + std_err = "contains a `debug()` or `debugonce()` statement." +) diff --git a/tests/testthat/test-hook-no-print-statement.R b/tests/testthat/test-hook-no-print-statement.R new file mode 100644 index 000000000..1a6a2e558 --- /dev/null +++ b/tests/testthat/test-hook-no-print-statement.R @@ -0,0 +1,13 @@ +# success +run_test( + "no-print-statement", + suffix = "-success.R", + std_err = NULL +) + +# failure +run_test( + "no-print-statement", + suffix = "-fail.R", + std_err = "contains a `print()` statement." +) diff --git a/tests/testthat/test-hook-parsable-R.R b/tests/testthat/test-hook-parsable-R.R new file mode 100644 index 000000000..a592c413d --- /dev/null +++ b/tests/testthat/test-hook-parsable-R.R @@ -0,0 +1,19 @@ +run_test("parsable-R", + suffix = "-success.R", + std_err = NULL +) + +run_test("parsable-R", + suffix = "-success.Rmd", + std_err = NULL +) + +# failure +run_test("parsable-R", suffix = "-fail.R", std_out = "Full context", std_err = "1 1") + +run_test( + "parsable-R", + suffix = "-fail.Rmd", + std_out = "parsable-R-fail.Rmd", + std_err = "1 1" +) diff --git a/tests/testthat/test-hook-pkgdown.R b/tests/testthat/test-hook-pkgdown.R new file mode 100644 index 000000000..dab3dd22b --- /dev/null +++ b/tests/testthat/test-hook-pkgdown.R @@ -0,0 +1,43 @@ +# success index +run_test("pkgdown", + file_name = c( + "man/autoudpate.Rd" = "autoupdate.Rd", + "_pkgdown.yml" = "_pkgdown-index.yml", + "DESCRIPTION" = "DESCRIPTION" + ), + suffix = "", std_err = NULL +) + +# failed index +run_test("pkgdown", + file_name = c( + "man/flie-true.Rd" = "flie-true.Rd", + "_pkgdown.yml" = "_pkgdown-index.yml", + "DESCRIPTION" = "DESCRIPTION" + ), + suffix = "", + std_err = "topic must be a known" +) + +# failed articles +run_test("pkgdown", + file_name = c( + "vignettes/pkgdown.Rmd" = "pkgdown.Rmd", + "_pkgdown.yml" = "_pkgdown-articles.yml", + "DESCRIPTION" = "DESCRIPTION" + ), + suffix = "", + std_err = "why-use-hooks" +) + +# success index and article +run_test("pkgdown", + file_name = c( + "man/autoudpate.Rd" = "autoupdate.Rd", + "vignettes/pkgdown.Rmd" = "pkgdown.Rmd", + "_pkgdown.yml" = "_pkgdown-index-articles.yml", + "DESCRIPTION" = "DESCRIPTION" + ), + suffix = "", + std_err = NULL +) diff --git a/tests/testthat/test-hook-readme-rmd-rendered.R b/tests/testthat/test-hook-readme-rmd-rendered.R new file mode 100644 index 000000000..7404acd08 --- /dev/null +++ b/tests/testthat/test-hook-readme-rmd-rendered.R @@ -0,0 +1,65 @@ +if (has_git()) { + run_test("readme-rmd-rendered", + file_name = c("README.md", "README.Rmd"), + suffix = "", + std_err = "out of date", + std_out = NULL, + file_transformer = function(files) { + if (length(files) > 1) { + # transformer is called once on all files and once per file + content_2 <- readLines(files[2]) + Sys.sleep(2) + writeLines(content_2, files[2]) + git_init() + git2r::add(path = files) + } + files + } + ) + + + # only one file staged + run_test("readme-rmd-rendered", + file_name = c("README.Rmd", "README.md"), + suffix = "", + std_err = "should be both staged", + std_out = NULL, + file_transformer = function(files) { + if (length(files) > 1) { + # transformer is called once on all files and once per file + content_2 <- readLines(files[2]) + Sys.sleep(2) + writeLines(content_2, files[2]) + git_init() + git2r::add(path = files[1]) + } + files + } + ) + + # only has md + run_test("readme-rmd-rendered", + file_name = "README.md", + suffix = "", + std_err = NULL, + std_out = NULL, + file_transformer = function(files) { + git_init() + git2r::add(path = files[1]) + files + } + ) + + # only has Rmd + run_test("readme-rmd-rendered", + file_name = "README.Rmd", + suffix = "", + std_err = NULL, + std_out = NULL, + file_transformer = function(files) { + git_init() + git2r::add(path = files[1]) + files + } + ) +} diff --git a/tests/testthat/test-hook-roxygenize.R b/tests/testthat/test-hook-roxygenize.R index 48567222a..ee6877b6c 100644 --- a/tests/testthat/test-hook-roxygenize.R +++ b/tests/testthat/test-hook-roxygenize.R @@ -1,161 +1,95 @@ -test_that("relevant diffs can be detected", { - withr::with_tempdir({ - fs::dir_create("R") - # when new lines are added - text <- c("#' Roxygen comment", "#'", "#' more things", "NULL") - writeLines(text, "R/first.R") - writeLines(text, "R/second.R") - git_init(".") - expect_false(diff_requires_run_roxygenize(".")) - git2r::add(".", "R/first.R") - git2r::status() - - expect_true(diff_requires_run_roxygenize(".")) - git2r::commit(".", "add roxgen2 file") - - # when non roxygen lines are added - text2 <- c("if (x) {", " TRUE", "} else {", " not_TRue(sinux(-i))", "}") - writeLines(text2, "R/third.R") - - git2r::add(".", "R/third.R") - expect_false(diff_requires_run_roxygenize(".")) - git2r::commit(".", "add non-roxygen2 file") - - # when roxygen line is replaced - text[1] <- "# not roxygen, but replaced old " - writeLines(text, "R/first.R") - writeLines(text[1], "R/fourth.R") - - git2r::add(".", c("R/first.R", "R/fourth.R")) - git2r::status() - expect_true(diff_requires_run_roxygenize(".")) - git2r::commit(".", "replaced") - - - # when roxygen line is removed - writeLines("#", "R/first.R") - - git2r::add(".", "R/first.R") - expect_true(diff_requires_run_roxygenize(".")) - git2r::commit(".", "when reomved") - }) -}) - -test_that("change in formals alone triggers invalidation", { - # when the function formals change but nothing else - withr::with_tempdir({ - fs::dir_create("R") - git_init(".") - # when new lines are added - text <- c("#' Roxygen comment", "#'", "#' more things", "x <- function(a = 2) {", " a", "}") - writeLines(text, "R/fifth.R") - expect_false(diff_requires_run_roxygenize(".")) - git2r::add(".", "R/fifth.R") - - expect_true(diff_requires_run_roxygenize(".")) - git2r::commit(".", "add file 5") - # change signature - text <- c("#' Roxygen comment", "#'", "#' more things", "x <- function(a = 3) {", " a", "}") - writeLines(text, "R/fifth.R") - git2r::add(".", "R/fifth.R") - git2r::commit(".", "clear case 5") - }) -}) - - -test_that("asserting installed dependencies", { - local_test_setup(git = FALSE, use_precommit = FALSE, package = TRUE) - installed <- c("pkgload", "rlang", "testthat") - purrr::walk(installed, usethis::use_package) - writeLines(c("utils::adist", "rlang::is_installed"), "R/blur.R") - testthat::expect_silent(roxygen_assert_additional_dependencies()) - writeLines(generate_uninstalled_pkg_call(), "R/core.R") - testthat::expect_error( - roxygen_assert_additional_dependencies(), - "there is no package called" - ) -}) - -test_that("roxygenize works in general", { - local_test_setup(git = FALSE, use_precommit = FALSE, package = TRUE) - writeLines(c("#' This is a title", "#'", "#' More", "#' @name test", "NULL"), "R/blur.R") - # works - mockery::stub(roxygenize_with_cache, "diff_requires_run_roxygenize", TRUE) - expect_message( - roxygenize_with_cache(list(getwd()), dirs = dirs_R.cache("roxygenize")), - "test\\.Rd" - ) -}) - - -test_that("fails when package is called but not installed in roclets", { - local_test_setup(git = FALSE, use_precommit = FALSE, package = TRUE) - writeLines(c("NULL"), "R/blur.R") - # works - mockery::stub(roxygenize_with_cache, "diff_requires_run_roxygenize", TRUE) - # when there is a missing package - roxygen_field <- paste0( - 'list(markdown = TRUE, roclets = c("rd", "namespace", "collate", "', - generate_uninstalled_pkg_call(), '"))' - ) - desc::desc_set(Roxygen = roxygen_field) - expect_error( - roxygenize_with_cache(list(getwd()), dirs = dirs_R.cache("roxygenize")), - "Please add the package as a dependency" - ) -}) - - -test_that("fails gratefully when not installed package is called (packageNotFoundError)", { - local_test_setup(git = FALSE, use_precommit = FALSE, package = TRUE) - writeLines(generate_uninstalled_pkg_call(), "R/blur.R") - # works - mockery::stub(roxygenize_with_cache, "diff_requires_run_roxygenize", TRUE) - # when there is a missing package - expect_error( - roxygenize_with_cache(list(getwd()), dirs = dirs_R.cache("roxygenize")), - "there is no package called" - ) -}) - -test_that("fails gratefully when not installed package is required according to `DESCRIPTION`", { - local_test_setup(git = FALSE, use_precommit = FALSE, package = TRUE) - desc::desc_set_deps( - tibble::tibble(type = "Imports", package = generate_uninstalled_pkg_name(), version = "*") - ) - mockery::stub(roxygenize_with_cache, "diff_requires_run_roxygenize", TRUE) - expect_error( - suppressWarnings( - roxygenize_with_cache(list(getwd()), dirs = dirs_R.cache("roxygenize")) +# with outdated Rd present +run_test("roxygenize", + file_name = c("man/flie.Rd" = "flie.Rd"), + suffix = "", + std_err = NA, + std_out = "Writing NAMESPACE", + artifacts = c( + "DESCRIPTION" = test_path("in/DESCRIPTION-no-deps.dcf"), + "R/roxygenize.R" = test_path("in/roxygenize.R") + ), + file_transformer = function(files) { + git_init() + git2r::add(path = files) + # hack to add artifact to trigger diff_requires_roxygenize() + git2r::add(path = fs::path(fs::path_dir(fs::path_dir(files[1])), "R")) + files + } +) + +if (!on_cran()) { + # with outdated Rd present in correct root + run_test("roxygenize", + file_name = c("rpkg/man/flie.Rd" = "flie.Rd"), + suffix = "", + std_err = NA, + cmd_args = "--root=rpkg", + std_out = "Writing NAMESPACE", + artifacts = c( + "rpkg/DESCRIPTION" = test_path("in/DESCRIPTION-no-deps.dcf"), + "rpkg/R/roxygenize.R" = test_path("in/roxygenize.R") ), - "The package .*required" - ) -}) - - -test_that("fails when there is invalid code", { - local_test_setup(git = FALSE, use_precommit = FALSE, package = TRUE) - mockery::stub(roxygenize_with_cache, "diff_requires_run_roxygenize", TRUE) - # when there is a missing package - writeLines(c("invalid code stuff /3kj"), "R/more.R") - expect_error( - roxygenize_with_cache(list(getwd()), dirs = dirs_R.cache("roxygenize")), - "[Uu]nexpected symbol" + file_transformer = function(files) { + withr::local_dir("rpkg") + git_init() + git2r::add(path = files) + # hack to add artifact to trigger diff_requires_roxygenize() + git2r::add(path = fs::path(fs::path_dir(fs::path_dir(files[1])), "R")) + files + } ) -}) - -test_that("warns if there is any other warning", { - local_test_setup(git = FALSE, use_precommit = FALSE, package = TRUE) - mockery::stub(roxygenize_with_cache, "diff_requires_run_roxygenize", TRUE) - writeLines( - c( - "#' This is a title", "#'", "#' More", "#", "NULL" + # without Rd present + run_test("roxygenize", + file_name = c("rpkg1/R/roxygenize.R" = "roxygenize.R"), + suffix = "", + cmd_args = "--root=rpkg1", + std_err = "Please commit the new `.Rd` files", + artifacts = c( + "rpkg1/DESCRIPTION" = test_path("in/DESCRIPTION-no-deps.dcf"), + "rpkg2/R/roxygenize.R" = test_path("in/roxygenize.R") ), - "R/blur.R" - ) - - expect_message( - roxygenize_with_cache(list(getwd()), dirs = dirs_R.cache("roxygenize")), - "(with|a) @name" + file_transformer = function(files) { + withr::local_dir("rpkg1") + git_init() + git2r::add(path = files) + files + } ) -}) +} + + + +# with Rd present in wrong root +run_test("roxygenize", + file_name = c("R/roxygenize.R" = "roxygenize.R"), + suffix = "", + std_err = "Please commit the new `.Rd` files", + artifacts = c( + "DESCRIPTION" = test_path("in/DESCRIPTION-no-deps.dcf") + ), + file_transformer = function(files) { + git_init() + git2r::add(path = files) + files + } +) + + +# with up to date rd present +run_test("roxygenize", + file_name = c("man/flie.Rd" = "flie-true.Rd"), + suffix = "", + std_err = "Writing NAMESPACE", + artifacts = c( + "DESCRIPTION" = test_path("in/DESCRIPTION-no-deps.dcf"), + "R/roxygenize.R" = test_path("in/roxygenize.R") + ), + file_transformer = function(files) { + git_init() + git2r::add(path = files) + # hack to add artifact to trigger diff_requires_roxygenize() + git2r::add(path = fs::path(fs::path_dir(fs::path_dir(files[1])), "R")) + files + }, + expect_success = TRUE +) diff --git a/tests/testthat/test-hook-spell-check.R b/tests/testthat/test-hook-spell-check.R new file mode 100644 index 000000000..f5e551bc3 --- /dev/null +++ b/tests/testthat/test-hook-spell-check.R @@ -0,0 +1,32 @@ +# success +run_test("spell-check", suffix = "-success.md", std_err = NULL) + +# failure with --read-only does not create WORDLIST +run_test( + "spell-check", + suffix = "-fail.md", + std_err = "Spell check failed", + cmd_args = "--read-only", + read_only = TRUE +) + +# failure with --read-only does not update WORDLIST +run_test( + "spell-check", + suffix = "-fail-2.md", + std_err = "Spell check failed", + cmd_args = "--read-only", + artifacts = c("inst/WORDLIST" = test_path("in/WORDLIST")), + read_only = TRUE +) + +# success with wordlist +run_test("spell-check", + suffix = "-wordlist-success.md", + std_err = NULL, + artifacts = c("inst/WORDLIST" = test_path("in/WORDLIST")) +) + +# success with ignored files +# uses lang argument +run_test("spell-check", suffix = "-language-success.md", cmd_args = "--lang=en_GB") diff --git a/tests/testthat/test-hook-style-files.R b/tests/testthat/test-hook-style-files.R new file mode 100644 index 000000000..f01cfb707 --- /dev/null +++ b/tests/testthat/test-hook-style-files.R @@ -0,0 +1,113 @@ +# success +run_test("style-files", + suffix = "-success.R", cmd_args = c("--cache-root=styler") +) +# fail +run_test("style-files", + suffix = "-fail-changed.R", cmd_args = c("--cache-root=styler"), + std_err = NA +) + +run_test("style-files", + suffix = "-fail-parse.R", cmd_args = c("--cache-root=styler"), + std_err = "" +) + +# success with cmd args +run_test("style-files", + file_name = "style-files-cmd", + suffix = "-success.R", + cmd_args = c("--style_pkg=styler", "--style_fun=tidyverse_style", "--cache-root=styler") +) + +run_test("style-files", + file_name = "style-files-cmd", + suffix = "-success.R", + cmd_args = c("--scope=spaces", "--cache-root=styler") +) + +run_test("style-files", + file_name = "style-files-cmd", + suffix = "-success.R", + cmd_args = c('--scope="I(\'spaces\')"', "--cache-root=styler") +) + +run_test("style-files", + file_name = "style-files-cmd", + suffix = "-success.R", + cmd_args = c( + '--scope="I(\'spaces\')"', + "--base_indention=0", + "--include_roxygen_examples=TRUE", + "--cache-root=styler" + ) +) + +run_test("style-files", + file_name = "style-files-reindention", + suffix = "-success.R", + cmd_args = c( + '--scope="I(\'spaces\')"', + "--base_indention=0", + "--include_roxygen_examples=TRUE", + '--reindention="specify_reindention(\'#\')"', + "--cache-root=styler" + ) +) + +run_test("style-files", + file_name = "style-files", + suffix = "-base-indention-success.R", + cmd_args = c("--base_indention=4", "--cache-root=styler") +) + +run_test("style-files", + file_name = "style-files", + suffix = "-roxygen-success.R", + cmd_args = c("--include_roxygen_examples=FALSE", "--cache-root=styler") +) + +run_test("style-files", + file_name = "style-files", + suffix = "-ignore-success.R", + cmd_args = c( + "--cache-root=styler", + '--ignore-start="^# styler: off$"', + '--ignore-stop="^# styler: on$"' + ) +) + +run_test("style-files", + file_name = "style-files", + suffix = "-ignore-fail.R", + cmd_args = "--cache-root=styler", + std_err = "" +) + + +# fail with cmd args +run_test("style-files", + file_name = "style-files-cmd", + suffix = "-success.R", + cmd_args = c("--scope=space", "--cache-root=styler"), + std_err = "", + expect_success = FALSE +) + +run_test("style-files", + file_name = "style-files-cmd", + suffix = "-fail.R", + std_err = NA, + cmd_args = c( + "--style_pkg=styler", "--style_fun=tidyverse_style", "--cache-root=styler" + ) +) + +run_test("style-files", + file_name = "style-files-cmd", + suffix = "-fail.R", + std_err = "must be listed in `additional_dependencies:`", + cmd_args = c( + "--style_pkg=blubliblax", "--style_fun=tidyverse_style", "--cache-root=styler" + ) +) diff --git a/tests/testthat/test-hook-use-tidy-description.R b/tests/testthat/test-hook-use-tidy-description.R new file mode 100644 index 000000000..2315d2030 --- /dev/null +++ b/tests/testthat/test-hook-use-tidy-description.R @@ -0,0 +1,23 @@ +# success +run_test("use-tidy-description", "DESCRIPTION", suffix = "") + + + +if (!on_cran()) { + # in sub directory with correct root + run_test("use-tidy-description", + "DESCRIPTION", + suffix = "", + cmd_args = "--root=rpkg", + artifacts = c("rpkg/DESCRIPTION" = test_path("in/DESCRIPTION")) + ) + # in sub directory with incorrect root + # Need to generate the directoy `rpkg` but without DESCRIPTION file. + run_test("use-tidy-description", + "DESCRIPTION", + suffix = "", + cmd_args = "--root=rpkg", + std_err = "No `DESCRIPTION` found in repository.", + artifacts = c("rpkg/README.md" = test_path("in/README.md")) + ) +} diff --git a/tests/testthat/test-hooks.R b/tests/testthat/test-hooks.R deleted file mode 100644 index 723e5cd49..000000000 --- a/tests/testthat/test-hooks.R +++ /dev/null @@ -1,706 +0,0 @@ -### . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. -### use-tidy-description #### - -# success -run_test("use-tidy-description", "DESCRIPTION", suffix = "") - -# in sub directory with correct root -run_test("use-tidy-description", - "DESCRIPTION", - suffix = "", - cmd_args = "--root=rpkg", - artifacts = c("rpkg/DESCRIPTION" = test_path("in/DESCRIPTION")) -) - - - -# in sub directory with incorrect root -# Need to generate the directoy `rpkg` but without DESCRIPTION file. -run_test("use-tidy-description", - "DESCRIPTION", - suffix = "", - cmd_args = "--root=rpkg", - std_err = "No `DESCRIPTION` found in repository.", - artifacts = c("rpkg/README.md" = test_path("in/README.md")) -) - -### . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. -### style-files #### - -# success -run_test("style-files", - suffix = "-success.R", cmd_args = c("--cache-root=styler") -) -# fail -run_test("style-files", - suffix = "-fail-changed.R", cmd_args = c("--cache-root=styler"), - std_err = NA -) - -run_test("style-files", - suffix = "-fail-parse.R", cmd_args = c("--cache-root=styler"), - std_err = "unexpected" -) - -# success with cmd args -run_test("style-files", - file_name = "style-files-cmd", - suffix = "-success.R", - cmd_args = c("--style_pkg=styler", "--style_fun=tidyverse_style", "--cache-root=styler") -) - -run_test("style-files", - file_name = "style-files-cmd", - suffix = "-success.R", - cmd_args = c("--scope=spaces", "--cache-root=styler") -) - -run_test("style-files", - file_name = "style-files-cmd", - suffix = "-success.R", - cmd_args = c('--scope="I(\'spaces\')"', "--cache-root=styler") -) - -run_test("style-files", - file_name = "style-files-cmd", - suffix = "-success.R", - cmd_args = c( - '--scope="I(\'spaces\')"', - "--base_indention=0", - "--include_roxygen_examples=TRUE", - "--cache-root=styler" - ) -) - -run_test("style-files", - file_name = "style-files-reindention", - suffix = "-success.R", - cmd_args = c( - '--scope="I(\'spaces\')"', - "--base_indention=0", - "--include_roxygen_examples=TRUE", - '--reindention="specify_reindention(\'#\')"', - "--cache-root=styler" - ) -) - -run_test("style-files", - file_name = "style-files", - suffix = "-base-indention-success.R", - cmd_args = c("--base_indention=4", "--cache-root=styler") -) - -run_test("style-files", - file_name = "style-files", - suffix = "-roxygen-success.R", - cmd_args = c("--include_roxygen_examples=FALSE", "--cache-root=styler") -) - -run_test("style-files", - file_name = "style-files", - suffix = "-ignore-success.R", - cmd_args = c( - "--cache-root=styler", - '--ignore-start="^# styler: off$"', - '--ignore-stop="^# styler: on$"' - ) -) - -run_test("style-files", - file_name = "style-files", - suffix = "-ignore-fail.R", - cmd_args = "--cache-root=styler", - std_err = "Invalid stylerignore sequences" -) - - -# fail with cmd args -run_test("style-files", - file_name = "style-files-cmd", - suffix = "-success.R", - std_err = "scope must be one", - cmd_args = c("--scope=space", "--cache-root=styler") -) - -run_test("style-files", - file_name = "style-files-cmd", - suffix = "-fail.R", - std_err = NA, - cmd_args = c( - "--style_pkg=styler", "--style_fun=tidyverse_style", "--cache-root=styler" - ) -) - -run_test("style-files", - file_name = "style-files-cmd", - suffix = "-fail.R", - std_err = "must be listed in `additional_dependencies:`", - cmd_args = c( - "--style_pkg=blubliblax", "--style_fun=tidyverse_style", "--cache-root=styler" - ) -) - -### . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. -### no-browser-statement #### -# success -run_test( - "no-browser-statement", - suffix = "-success.R", - std_err = NULL -) - -# failure -run_test( - "no-browser-statement", - suffix = "-fail.R", - std_err = "contains a `browser()` statement." -) - -### . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. -### no-print-statement #### -# success -run_test( - "no-print-statement", - suffix = "-success.R", - std_err = NULL -) - -# failure -run_test( - "no-print-statement", - suffix = "-fail.R", - std_err = "contains a `print()` statement." -) - -### . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. -### no-debug-statement #### -# success -run_test( - "no-debug-statement", - suffix = "-success.R", - std_err = NULL -) - -# failure -run_test( - "no-debug-statement", - suffix = "-fail.R", - std_err = "contains a `debug()` or `debugonce()` statement." -) - - -### . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. -### parsable-R #### - -# success -run_test("parsable-R", - suffix = "-success.R", - std_err = NULL -) - -run_test("parsable-R", - suffix = "-success.Rmd", - std_err = NULL -) - -# failure -run_test("parsable-R", suffix = "-fail.R", std_out = "Full context", std_err = "1 1") - -run_test( - "parsable-R", - suffix = "-fail.Rmd", - std_out = "parsable-R-fail.Rmd", - std_err = "1 1" -) - - -### . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. -### spell-check #### -# success -run_test("spell-check", suffix = "-success.md", std_err = NULL) - -# failure with --read-only does not create WORDLIST -run_test( - "spell-check", - suffix = "-fail.md", - std_err = "Spell check failed", - cmd_args = "--read-only", - read_only = TRUE -) - -# failure with --read-only does not update WORDLIST -run_test( - "spell-check", - suffix = "-fail-2.md", - std_err = "Spell check failed", - cmd_args = "--read-only", - artifacts = c("inst/WORDLIST" = test_path("in/WORDLIST")), - read_only = TRUE -) - -# success with wordlist -run_test("spell-check", - suffix = "-wordlist-success.md", - std_err = NULL, - artifacts = c("inst/WORDLIST" = test_path("in/WORDLIST")) -) - -# success with ignored files -# uses lang argument -run_test("spell-check", suffix = "-language-success.md", cmd_args = "--lang=en_GB") - - - -### . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. -### depds-in-desc #### -# succeed (call to library that is in description) -run_test("deps-in-desc", - suffix = "-success.R", std_err = NULL, - artifacts = c("DESCRIPTION" = test_path("in/DESCRIPTION")) -) - -# fail (call to library that is not in description) -run_test("deps-in-desc", - suffix = "-fail.R", std_err = "Dependency check failed", - artifacts = c("DESCRIPTION" = test_path("in/DESCRIPTION")) -) - -# in sub directory with wrong root -run_test("deps-in-desc", - suffix = "-fail.R", std_err = "Could not find R package", - file_transformer = function(files) { - fs::path_abs(fs::file_move(files, "rpkg")) - }, - artifacts = c("rpkg/DESCRIPTION" = test_path("in/DESCRIPTION")) -) - -# in sub directory with correct root -run_test("deps-in-desc", - cmd_args = "--root=rpkg", - suffix = "-fail.R", std_err = "Dependency check failed", - file_transformer = function(files) { - fs::path_abs(fs::file_move(files, "rpkg")) - }, - artifacts = c("rpkg/DESCRIPTION" = test_path("in/DESCRIPTION")) -) - -# in sub directory with correct root -run_test("deps-in-desc", - cmd_args = "--root=rpkg", - suffix = "-success.R", std_err = NULL, - file_transformer = function(files) { - fs::path_abs(fs::file_move(files, "rpkg")) - }, - artifacts = c("rpkg/DESCRIPTION" = test_path("in/DESCRIPTION")) -) - -# with ::: -run_test("deps-in-desc", - "deps-in-desc-dot3", - suffix = "-fail.R", std_err = "Dependency check failed", - artifacts = c("DESCRIPTION" = test_path("in/DESCRIPTION")) -) - -run_test("deps-in-desc", - "deps-in-desc-dot3", - suffix = "-success.R", std_err = NULL, - artifacts = c("DESCRIPTION" = test_path("in/DESCRIPTION")) -) - -run_test("deps-in-desc", - "deps-in-desc-dot3", - suffix = "-fail.R", std_err = NULL, - artifacts = c("DESCRIPTION" = test_path("in/DESCRIPTION")), - cmd_args = "--allow_private_imports" -) - -# Rmd -run_test("deps-in-desc", - "deps-in-desc", - suffix = "-fail.Rmd", std_err = "Dependency check failed", - std_out = "deps-in-desc-fail.Rmd`: ttyzp", - artifacts = c("DESCRIPTION" = test_path("in/DESCRIPTION")) -) - -run_test("deps-in-desc", - "deps-in-desc", - suffix = "-success.Rmd", std_err = NULL, - artifacts = c("DESCRIPTION" = test_path("in/DESCRIPTION")) -) - -# README.Rmd is excluded -run_test("deps-in-desc", - "README.Rmd", - suffix = "", std_err = NULL, - artifacts = c("DESCRIPTION" = test_path("in/DESCRIPTION-no-deps.dcf")) -) - - - -# Rnw -run_test("deps-in-desc", - "deps-in-desc", - suffix = "-fail.Rnw", std_err = "Dependency check failed", - artifacts = c("DESCRIPTION" = test_path("in/DESCRIPTION")) -) - -run_test("deps-in-desc", - "deps-in-desc", - suffix = "-success.Rnw", std_err = NULL, - artifacts = c("DESCRIPTION" = test_path("in/DESCRIPTION")) -) - -# Rprofile -# because .Rprofile is executed on startup, this must be an installed -# package (to not give an error staight away) not listed in -# test_path("in/DESCRIPTION") -if (Sys.getenv("GITHUB_WORKFLOW") != "Hook tests") { - # seems like .Rprofile with renv activation does not get executed when - # argument to Rscript contains Rprofile ?! Skip this - expect_true(rlang::is_installed("R.cache")) - run_test("deps-in-desc", - "Rprofile", - suffix = "", std_err = "Dependency check failed", - artifacts = c("DESCRIPTION" = test_path("in/DESCRIPTION")), - file_transformer = function(files) { - writeLines("R.cache::findCache", files) - fs::file_move( - files, - fs::path(fs::path_dir(files), paste0(".", fs::path_file(files))) - ) - } - ) - - run_test("deps-in-desc", - "Rprofile", - suffix = "", std_err = NULL, - artifacts = c("DESCRIPTION" = test_path("in/DESCRIPTION")), - file_transformer = function(files) { - writeLines("utils::head", files) - fs::file_move( - files, - fs::path(fs::path_dir(files), paste0(".", fs::path_file(files))) - ) - } - ) -} - - -### . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. -### lintr #### - -# success -run_test("lintr", - suffix = "-success.R", - std_err = NULL -) - -# failure -run_test("lintr", suffix = "-fail.R", std_err = "not lint free") - -# warning -run_test( - "lintr", - suffix = "-fail.R", cmd_args = "--warn_only", std_err = NULL -) - -### . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. -### roxygenize #### -# with outdated Rd present -run_test("roxygenize", - file_name = c("man/flie.Rd" = "flie.Rd"), - suffix = "", - std_err = NA, - std_out = "Writing NAMESPACE", - artifacts = c( - "DESCRIPTION" = test_path("in/DESCRIPTION-no-deps.dcf"), - "R/roxygenize.R" = test_path("in/roxygenize.R") - ), - file_transformer = function(files) { - git_init() - git2r::add(path = files) - # hack to add artifact to trigger diff_requires_roxygenize() - git2r::add(path = fs::path(fs::path_dir(fs::path_dir(files[1])), "R")) - files - } -) - -# with outdated Rd present in correct root -run_test("roxygenize", - file_name = c("rpkg/man/flie.Rd" = "flie.Rd"), - suffix = "", - std_err = NA, - cmd_args = "--root=rpkg", - std_out = "Writing NAMESPACE", - artifacts = c( - "rpkg/DESCRIPTION" = test_path("in/DESCRIPTION-no-deps.dcf"), - "rpkg/R/roxygenize.R" = test_path("in/roxygenize.R") - ), - file_transformer = function(files) { - withr::local_dir("rpkg") - git_init() - git2r::add(path = files) - # hack to add artifact to trigger diff_requires_roxygenize() - git2r::add(path = fs::path(fs::path_dir(fs::path_dir(files[1])), "R")) - files - } -) - - -# without Rd present -run_test("roxygenize", - file_name = c("rpkg1/R/roxygenize.R" = "roxygenize.R"), - suffix = "", - cmd_args = "--root=rpkg1", - std_err = "Please commit the new `.Rd` files", - artifacts = c( - "rpkg1/DESCRIPTION" = test_path("in/DESCRIPTION-no-deps.dcf"), - "rpkg2/R/roxygenize.R" = test_path("in/roxygenize.R") - ), - file_transformer = function(files) { - withr::local_dir("rpkg1") - git_init() - git2r::add(path = files) - files - } -) - -# with Rd present in wrong root -run_test("roxygenize", - file_name = c("R/roxygenize.R" = "roxygenize.R"), - suffix = "", - std_err = "Please commit the new `.Rd` files", - artifacts = c( - "DESCRIPTION" = test_path("in/DESCRIPTION-no-deps.dcf") - ), - file_transformer = function(files) { - git_init() - git2r::add(path = files) - files - } -) - - -# with up to date rd present -run_test("roxygenize", - file_name = c("man/flie.Rd" = "flie-true.Rd"), - suffix = "", - std_err = "Writing NAMESPACE", - artifacts = c( - "DESCRIPTION" = test_path("in/DESCRIPTION-no-deps.dcf"), - "R/roxygenize.R" = test_path("in/roxygenize.R") - ), - file_transformer = function(files) { - git_init() - git2r::add(path = files) - # hack to add artifact to trigger diff_requires_roxygenize() - git2r::add(path = fs::path(fs::path_dir(fs::path_dir(files[1])), "R")) - files - }, - expect_success = TRUE -) - -### . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. -### codemeata #### -run_test("codemeta-description-update", - file_name = c("codemeta.json"), - suffix = "", - std_err = "No `DESCRIPTION` found in repository.", - std_out = NULL, -) - -run_test("codemeta-description-update", - file_name = c("DESCRIPTION"), - suffix = "", - std_err = "No `codemeta.json` found in repository.", - std_out = NULL, -) - - -# outdated -run_test("codemeta-description-update", - file_name = c("DESCRIPTION", "codemeta.json"), - suffix = "", - std_err = "out of date", - std_out = NULL, - file_transformer = function(files) { - if (length(files) > 1) { - # transformer is called once on all files and once per file - content_2 <- readLines(files[1]) - Sys.sleep(2) - writeLines(content_2, files[1]) - } - files - } -) - -# succeed -run_test("codemeta-description-update", - file_name = c("DESCRIPTION", "codemeta.json"), - suffix = "", - file_transformer = function(files) { - if (length(files) > 1) { - # transformer is called once on all files and once per file - content_2 <- readLines(files[2]) - Sys.sleep(2) - writeLines(content_2, files[2]) - } - files - } -) - -# succeed in correct root -run_test("codemeta-description-update", - file_name = c( - "rpkg/DESCRIPTION" = "DESCRIPTION", - "rpkg/codemeta.json" = "codemeta.json" - ), - cmd_args = "--root=rpkg", - suffix = "", - file_transformer = function(files) { - if (length(files) > 1) { - # transformer is called once on all files and once per file - content_2 <- readLines(files[2]) - Sys.sleep(2) - writeLines(content_2, files[2]) - } - files - } -) - -# # fail in wrong root -run_test("codemeta-description-update", - file_name = c( - "rpkg/DESCRIPTION" = "DESCRIPTION", - "rpkg/codemeta.json" = "codemeta.json", - "rpkg2/codemeta.json" = "README.md" - ), - cmd_args = "--root=rpkg2", - std_err = "No `DESCRIPTION` found in repository.", - suffix = "", - file_transformer = function(files) { - if (length(files) > 1) { - # transformer is called once on all files and once per file - content_2 <- readLines(files[2]) - Sys.sleep(2) - writeLines(content_2, files[2]) - } - files - } -) - -### . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. -### pgkdown check #### - -# success index -run_test("pkgdown", - file_name = c( - "man/autoudpate.Rd" = "autoupdate.Rd", - "_pkgdown.yml" = "_pkgdown-index.yml", - "DESCRIPTION" = "DESCRIPTION" - ), - suffix = "", std_err = NULL -) - -# failed index -run_test("pkgdown", - file_name = c( - "man/flie-true.Rd" = "flie-true.Rd", - "_pkgdown.yml" = "_pkgdown-index.yml", - "DESCRIPTION" = "DESCRIPTION" - ), - suffix = "", - std_err = "topic must be a known" -) - -# failed articles -run_test("pkgdown", - file_name = c( - "vignettes/pkgdown.Rmd" = "pkgdown.Rmd", - "_pkgdown.yml" = "_pkgdown-articles.yml", - "DESCRIPTION" = "DESCRIPTION" - ), - suffix = "", - std_err = "why-use-hooks" -) - -# success index and article -run_test("pkgdown", - file_name = c( - "man/autoudpate.Rd" = "autoupdate.Rd", - "vignettes/pkgdown.Rmd" = "pkgdown.Rmd", - "_pkgdown.yml" = "_pkgdown-index-articles.yml", - "DESCRIPTION" = "DESCRIPTION" - ), - suffix = "", - std_err = NULL -) - - -### . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. -### readme-rmd-rendered #### -if (has_git()) { - run_test("readme-rmd-rendered", - file_name = c("README.md", "README.Rmd"), - suffix = "", - std_err = "out of date", - std_out = NULL, - file_transformer = function(files) { - if (length(files) > 1) { - # transformer is called once on all files and once per file - content_2 <- readLines(files[2]) - Sys.sleep(2) - writeLines(content_2, files[2]) - git_init() - git2r::add(path = files) - } - files - } - ) - - - # only one file staged - run_test("readme-rmd-rendered", - file_name = c("README.Rmd", "README.md"), - suffix = "", - std_err = "should be both staged", - std_out = NULL, - file_transformer = function(files) { - if (length(files) > 1) { - # transformer is called once on all files and once per file - content_2 <- readLines(files[2]) - Sys.sleep(2) - writeLines(content_2, files[2]) - git_init() - git2r::add(path = files[1]) - } - files - } - ) - - # only has md - run_test("readme-rmd-rendered", - file_name = "README.md", - suffix = "", - std_err = NULL, - std_out = NULL, - file_transformer = function(files) { - git_init() - git2r::add(path = files[1]) - files - } - ) - - # only has Rmd - run_test("readme-rmd-rendered", - file_name = "README.Rmd", - suffix = "", - std_err = NULL, - std_out = NULL, - file_transformer = function(files) { - git_init() - git2r::add(path = files[1]) - files - } - ) -} diff --git a/tests/testthat/test-impl-hook-roxygenize.R b/tests/testthat/test-impl-hook-roxygenize.R new file mode 100644 index 000000000..eb79361e3 --- /dev/null +++ b/tests/testthat/test-impl-hook-roxygenize.R @@ -0,0 +1,169 @@ +test_that("relevant diffs can be detected", { + skip_on_cran() + withr::with_tempdir({ + fs::dir_create("R") + # when new lines are added + text <- c("#' Roxygen comment", "#'", "#' more things", "NULL") + writeLines(text, "R/first.R") + writeLines(text, "R/second.R") + git_init(".") + expect_false(diff_requires_run_roxygenize(".")) + git2r::add(".", "R/first.R") + git2r::status() + + expect_true(diff_requires_run_roxygenize(".")) + git2r::commit(".", "add roxgen2 file") + + # when non roxygen lines are added + text2 <- c("if (x) {", " TRUE", "} else {", " not_TRue(sinux(-i))", "}") + writeLines(text2, "R/third.R") + + git2r::add(".", "R/third.R") + expect_false(diff_requires_run_roxygenize(".")) + git2r::commit(".", "add non-roxygen2 file") + + # when roxygen line is replaced + text[1] <- "# not roxygen, but replaced old " + writeLines(text, "R/first.R") + writeLines(text[1], "R/fourth.R") + + git2r::add(".", c("R/first.R", "R/fourth.R")) + git2r::status() + expect_true(diff_requires_run_roxygenize(".")) + git2r::commit(".", "replaced") + + + # when roxygen line is removed + writeLines("#", "R/first.R") + + git2r::add(".", "R/first.R") + expect_true(diff_requires_run_roxygenize(".")) + git2r::commit(".", "when reomved") + }) +}) + +test_that("change in formals alone triggers invalidation", { + skip_on_cran() + # when the function formals change but nothing else + withr::with_tempdir({ + fs::dir_create("R") + git_init(".") + # when new lines are added + text <- c("#' Roxygen comment", "#'", "#' more things", "x <- function(a = 2) {", " a", "}") + writeLines(text, "R/fifth.R") + expect_false(diff_requires_run_roxygenize(".")) + git2r::add(".", "R/fifth.R") + + expect_true(diff_requires_run_roxygenize(".")) + git2r::commit(".", "add file 5") + # change signature + text <- c("#' Roxygen comment", "#'", "#' more things", "x <- function(a = 3) {", " a", "}") + writeLines(text, "R/fifth.R") + git2r::add(".", "R/fifth.R") + git2r::commit(".", "clear case 5") + }) +}) + + +test_that("asserting installed dependencies", { + local_test_setup(git = FALSE, use_precommit = FALSE, package = TRUE) + installed <- c("pkgload", "rlang", "testthat") + purrr::walk(installed, usethis::use_package) + writeLines(c("utils::adist", "rlang::is_installed"), "R/blur.R") + testthat::expect_silent(roxygen_assert_additional_dependencies()) + writeLines(generate_uninstalled_pkg_call(), "R/core.R") + testthat::expect_error( + roxygen_assert_additional_dependencies(), + "there is no package called" + ) +}) + +test_that("roxygenize works in general", { + skip_on_cran() + local_test_setup(git = FALSE, use_precommit = FALSE, package = TRUE) + writeLines(c("#' This is a title", "#'", "#' More", "#' @name test", "NULL"), "R/blur.R") + # works + local_mocked_bindings(diff_requires_run_roxygenize = function(...) TRUE) + expect_message( + roxygenize_with_cache(list(getwd()), dirs = dirs_R.cache("roxygenize")), + "test\\.Rd" + ) +}) + + +test_that("fails when package is called but not installed in roclets", { + skip_on_cran() + local_test_setup(git = FALSE, use_precommit = FALSE, package = TRUE) + writeLines(c("NULL"), "R/blur.R") + # works + local_mocked_bindings(diff_requires_run_roxygenize = function(...) TRUE) + # when there is a missing package + roxygen_field <- paste0( + 'list(markdown = TRUE, roclets = c("rd", "namespace", "collate", "', + generate_uninstalled_pkg_call(), '"))' + ) + desc::desc_set(Roxygen = roxygen_field) + expect_error( + roxygenize_with_cache(list(getwd()), dirs = dirs_R.cache("roxygenize")), + "Please add the package as a dependency" + ) +}) + + +test_that("fails gratefully when not installed package is called (packageNotFoundError)", { + skip_on_cran() + local_test_setup(git = FALSE, use_precommit = FALSE, package = TRUE) + writeLines(generate_uninstalled_pkg_call(), "R/blur.R") + # works + local_mocked_bindings(diff_requires_run_roxygenize = function(...) TRUE) + # when there is a missing package + expect_error( + roxygenize_with_cache(list(getwd()), dirs = dirs_R.cache("roxygenize")), + "there is no package called" + ) +}) + +test_that("fails gratefully when not installed package is required according to `DESCRIPTION`", { + skip_on_cran() + local_test_setup(git = FALSE, use_precommit = FALSE, package = TRUE) + desc::desc_set_deps( + tibble::tibble(type = "Imports", package = generate_uninstalled_pkg_name(), version = "*") + ) + local_mocked_bindings(diff_requires_run_roxygenize = function(...) TRUE) + expect_error( + suppressWarnings( + roxygenize_with_cache(list(getwd()), dirs = dirs_R.cache("roxygenize")) + ), + "The package.*required" + ) +}) + + +test_that("fails when there is invalid code", { + skip_on_cran() + local_test_setup(git = FALSE, use_precommit = FALSE, package = TRUE) + local_mocked_bindings(diff_requires_run_roxygenize = function(...) TRUE) + # when there is a missing package + writeLines(c("invalid code stuff /3kj"), "R/more.R") + expect_error( + roxygenize_with_cache(list(getwd()), dirs = dirs_R.cache("roxygenize")), + "[Uu]nexpected symbol" + ) +}) + +test_that("warns if there is any other warning", { + skip_on_cran() + local_test_setup(git = FALSE, use_precommit = FALSE, package = TRUE) + local_mocked_bindings(diff_requires_run_roxygenize = function(...) TRUE) + writeLines( + c( + "#' This is a title", "#'", "#' More", "#", "NULL" + ), + "R/blur.R" + ) + + expect_message( + roxygenize_with_cache(list(getwd()), dirs = dirs_R.cache("roxygenize")), + "(with|a) @name" + ) +}) diff --git a/tests/testthat/test-hooks-regex-exclude.R b/tests/testthat/test-impl-hooks-regex-exclude.R similarity index 100% rename from tests/testthat/test-hooks-regex-exclude.R rename to tests/testthat/test-impl-hooks-regex-exclude.R diff --git a/tests/testthat/test-setup.R b/tests/testthat/test-setup.R index f7dc73f1b..a2519755e 100644 --- a/tests/testthat/test-setup.R +++ b/tests/testthat/test-setup.R @@ -52,7 +52,7 @@ test_that("GitHub Action CI setup works", { root = getwd(), open = FALSE, verbose = FALSE ) - expect_error(use_ci("stuff"), "must be one of") + expect_error(use_ci("stuff", root = getwd()), "must be one of") use_ci("gha", root = getwd()) expect_true(file_exists(".github/workflows/pre-commit.yaml")) }) @@ -82,40 +82,3 @@ test_that("Pre-commit CI setup works", { ) expect_error(use_ci(root = getwd(), open = FALSE), "o `.pre-commit-config.yaml`") }) - - -test_that("Autoupdate is not conducted when renv present in incompatible setup", { - skip_on_cran() - - # mock old pre-commit and renv versions - mockery::stub(ensure_renv_precommit_compat, "version_precommit", "2.13.0") - - local_test_setup( - git = TRUE, use_precommit = TRUE, install_hooks = FALSE, open = FALSE - ) - initial <- rev_read() %>% - rev_as_pkg_version() - # simulate adding {renv} - writeLines("", "renv.lock") - - # should downgrade rev - expect_error( - ensure_renv_precommit_compat( - package_version_renv = package_version("0.13.0"), root = getwd() - ), - "Please update" - ) - downgraded <- rev_read() %>% - rev_as_pkg_version() - expect_true(downgraded == initial) - - # simulate removing {renv} should be updated - fs::file_delete("renv.lock") - expect_warning( - ensure_renv_precommit_compat( - package_version("0.13.0"), - root = getwd() - ), - NA - ) -}) diff --git a/vignettes/available-hooks.Rmd b/vignettes/available-hooks.Rmd index 572fd2e52..47b5720e1 100644 --- a/vignettes/available-hooks.Rmd +++ b/vignettes/available-hooks.Rmd @@ -355,6 +355,6 @@ dependencies and system libraries. For this check, we rely on the the global R package library and require all development dependencies of the package you want to run this hook for to be -installed, as well as {pkgdown} (without its dependencies) and {mockery}. +installed, as well as {pkgdown} (without its dependencies). This hook does not modify files. Added in version 0.3.2.9003.