Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# testthat (development version)

* In `R CMD check`, snapshots now only advise on how to resolve failures once (#2207).
* `snapshot_review()` includes a reject button and only displays the file navigation and the skip button if there are multiple files to review (#2025).
* New `snapshot_download_gh()` makes it easy to get snapshots off GitHub and into your local package (#1779).
* New `local_mocked_s3_method()`, `local_mocked_s4_method()`, and `local_mocked_r6_class()` allow you to mock S3 and S4 methods and R6 classes (#1892, #1916)
Expand Down
23 changes: 15 additions & 8 deletions R/expect-that.R
Original file line number Diff line number Diff line change
Expand Up @@ -41,18 +41,25 @@ fail <- function(
trace_env = caller_env(),
trace = NULL
) {
if (is.null(trace)) {
trace <- trace_back(top = getOption("testthat_topenv"), bottom = trace_env)
}
# Only show if there's at least one function apart from the expectation
if (trace_length(trace) <= 1) {
trace <- NULL
}

trace <- trace %||% capture_trace(trace_env)
message <- paste(c(message, info), collapse = "\n")
expectation("failure", message, srcref = srcref, trace = trace)
}

snapshot_fail <- function(message, trace_env = caller_env()) {
trace <- capture_trace(trace_env)
expectation("failure", message, trace = trace, snapshot = TRUE)
}

capture_trace <- function(trace_env) {
trace <- trace_back(top = getOption("testthat_topenv"), bottom = trace_env)
# Only include trace if there's at least one function apart from the expectation
if (!is.null(trace) && trace_length(trace) <= 1) {
trace <- NULL
}
trace
}

#' @rdname fail
#' @param value Value to return, typically the result of evaluating the
#' `object` argument to the expectation.
Expand Down
4 changes: 2 additions & 2 deletions R/expectation.R
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@ expect <- function(
#' @keywords internal
#' @inheritParams expect
#' @export
expectation <- function(type, message, srcref = NULL, trace = NULL) {
exp <- new_expectation(type, message, srcref = srcref, trace = trace)
expectation <- function(type, message, ..., srcref = NULL, trace = NULL) {
exp <- new_expectation(type, message, ..., srcref = srcref, trace = trace)
exp_signal(exp)
}
#' @rdname expectation
Expand Down
46 changes: 46 additions & 0 deletions R/reporter-check.R
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ CheckReporter <- R6::R6Class(
skips = NULL,
warnings = NULL,
n_ok = 0L,
old_in_check_reporter = NULL,

initialize = function(...) {
self$capabilities$parallel_support <- TRUE
Expand All @@ -35,7 +36,14 @@ CheckReporter <- R6::R6Class(
}
},

start_reporter = function() {
self$old_in_check_reporter <- in_check_reporter()
the$in_check_reporter <- TRUE
},

end_reporter = function() {
the$in_check_reporter <- self$old_in_check_reporter

if (self$skips$size() || self$warnings$size() || self$problems$size()) {
self$cat_line(summary_line(
n_fail = self$problems$size(),
Expand Down Expand Up @@ -64,6 +72,11 @@ CheckReporter <- R6::R6Class(
self$rule("Failed tests", line = 2)
self$cat_line(map_chr(problems, issue_summary, rule = TRUE))
self$cat_line()

if (some(problems, \(x) isTRUE(attr(x, "snapshot")))) {
self$rule("To resolve snapshot failures", line = 1)
self$cat_line(snapshot_check_hint())
}
} else {
# clean up
unlink("testthat-problems.rds")
Expand Down Expand Up @@ -105,3 +118,36 @@ summary_line <- function(n_fail, n_warn, n_skip, n_pass) {
" ]"
)
}

snapshot_check_hint <- function() {
if (on_gh()) {
repository <- Sys.getenv("GITHUB_REPOSITORY")
run_id <- Sys.getenv("GITHUB_RUN_ID")

call <- sprintf(
"testthat::snapshot_download_gh(\"%s\", \"%s\")",
repository,
run_id
)
copy <- sprintf("* Run `%s` to download snapshots.", call)
} else {
copy <- c(
if (on_ci()) {
"* Download and unzip artifact."
} else {
"* Locate check directory."
},
"* Copy 'tests/testthat/_snaps' to local package."
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the wording here can be misleading. Not just this line, the whole copy part. A failing snapshot does not necessarily mean that you need to update the snapshots, maybe it is a genuine test failure. (If I understand correctly when this is invoked.) Or it is a problem of platform dependent snapshots, in which case you can't simply update the snapshot files.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmm, yeah the advice is supposed to more about how to diagnose the failure locally. I'll tweak.

)
}

action <- c(
"* Run `testthat::snapshot_accept()` to accept all changes.",
"* Run `testthat::snapshot_review()` to review all changes."
)
c(copy, action)
}

run <- function(x) {
cli::format_inline(paste0("{.run testthat::", x, "}"))
}
45 changes: 10 additions & 35 deletions R/snapshot-file.R
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,11 @@ expect_snapshot_file <- function(
return(equal)
}

hint <- snapshot_review_hint(snapshotter$file, name)
if (in_check_reporter()) {
hint <- ""
} else {
hint <- snapshot_review_hint(snapshotter$file, name)
}

if (!equal) {
msg <- sprintf(
Expand All @@ -151,7 +155,7 @@ expect_snapshot_file <- function(
paste0(snapshotter$file, "/", name),
hint
)
return(fail(msg))
return(snapshot_fail(msg))
}
pass(NULL)
}
Expand All @@ -167,45 +171,16 @@ announce_snapshot_file <- function(path, name = basename(path)) {
}
}

snapshot_review_hint <- function(
test,
name,
ci = on_ci(),
check = in_rcmd_check(),
reset_output = TRUE
) {
snapshot_review_hint <- function(test, name, reset_output = TRUE) {
if (reset_output) {
local_reporter_output()
}

path <- paste0("tests/testthat/_snaps/", test, "/", new_name(name))

if (check) {
if (on_gh()) {
bullets <- snap_download_hint()
} else {
bullets <- c(
if (ci) "* Download and unzip run artifact\n",
if (!ci) "* Locate check directory\n",
paste0("* Copy '", path, "' to local test directory\n")
)
}
} else {
bullets <- NULL
}

paste0(
c(
bullets,
cli::format_inline(
"* Run {.run testthat::snapshot_review('{test}/')} to review changes"
)
),
collapse = ""
cli::format_inline(
"* Run {.run testthat::snapshot_review('{test}/{name}')} to review the change."
)
}


snapshot_file_equal <- function(
snap_dir, # _snaps/
snap_test, # test file name
Expand Down Expand Up @@ -254,7 +229,7 @@ snapshot_file_equal <- function(
# We want to fail on CI since this suggests that the user has failed
# to record the value locally
if (fail_on_new) {
return(fail(message, trace_env = trace_env))
return(snapshot_fail(message, trace_env = trace_env))
}
testthat_warn(message)
TRUE
Expand Down
11 changes: 0 additions & 11 deletions R/snapshot-github.R
Original file line number Diff line number Diff line change
Expand Up @@ -40,17 +40,6 @@ snapshot_download_gh <- function(repository, run_id, dest_dir = ".") {
dir_copy(src_snaps, dest_snaps)
}

snap_download_hint <- function() {
repository <- Sys.getenv("GITHUB_REPOSITORY")
run_id <- Sys.getenv("GITHUB_RUN_ID")

sprintf(
"* Call `snapshot_download_gh(\"%s\", \"%s\")` to download the snapshots from GitHub.\n",
repository,
run_id
)
}

gh_find_job <- function(repository, run_id) {
jobs_json <- gh::gh(
"/repos/{repository}/actions/runs/{run_id}/jobs",
Expand Down
13 changes: 8 additions & 5 deletions R/snapshot.R
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,7 @@ expect_snapshot_condition_ <- function(
class
)
}
return(fail(msg, trace_env = trace_env))
return(snapshot_fail(msg, trace_env = trace_env))
}

expect_snapshot_helper(
Expand Down Expand Up @@ -340,7 +340,11 @@ expect_snapshot_helper <- function(
} else {
variant_lab <- ""
}
hint <- snapshot_accept_hint(variant, snapshotter$file)
if (in_check_reporter()) {
hint <- ""
} else {
hint <- snapshot_accept_hint(variant, snapshotter$file)
}

if (length(comp) != 0) {
msg <- sprintf(
Expand All @@ -350,7 +354,7 @@ expect_snapshot_helper <- function(
paste0(comp, collapse = "\n\n"),
hint
)
return(fail(msg, trace_env = trace_env))
return(snapshot_fail(msg, trace_env = trace_env))
}

pass(NULL)
Expand All @@ -368,13 +372,12 @@ snapshot_accept_hint <- function(variant, file, reset_output = TRUE) {
}

paste0(
if (on_gh()) snap_download_hint(),
cli::format_inline(
"* Run {.run testthat::snapshot_accept('{name}')} to accept the change."
),
"\n",
cli::format_inline(
"* Run {.run testthat::snapshot_review('{name}')} to interactively review the change."
"* Run {.run testthat::snapshot_review('{name}')} to review the change."
)
)
}
Expand Down
1 change: 1 addition & 0 deletions R/testthat-package.R
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ the <- new.env(parent = emptyenv())
the$description <- character()
the$top_level_test <- TRUE
the$test_expectations <- 0
the$in_check_reporter <- FALSE

# The following block is used by usethis to automatically manage
# roxygen namespace tags. Modify with care!
Expand Down
4 changes: 2 additions & 2 deletions R/utils.R
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ first_upper <- function(x) {
x
}

in_rcmd_check <- function() {
nzchar(Sys.getenv("_R_CHECK_PACKAGE_NAME_", ""))
in_check_reporter <- function() {
isTRUE(the$in_check_reporter)
}

r_version <- function() paste0("R", getRversion()[, 1:2])
Expand Down
6 changes: 3 additions & 3 deletions man/expectation.Rd

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

29 changes: 29 additions & 0 deletions tests/testthat/_snaps/reporter-check.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,3 +96,32 @@

[ FAIL 4 | WARN 1 | SKIP 3 | PASS 1 ]

# generates informative snapshot hints

Code
base::writeLines(snapshot_check_hint())
Output
* Locate check directory.
* Copy 'tests/testthat/_snaps' to local package.
* Run `testthat::snapshot_accept()` to accept all changes.
* Run `testthat::snapshot_review()` to review all changes.

---

Code
base::writeLines(snapshot_check_hint())
Output
* Download and unzip artifact.
* Copy 'tests/testthat/_snaps' to local package.
* Run `testthat::snapshot_accept()` to accept all changes.
* Run `testthat::snapshot_review()` to review all changes.

---

Code
base::writeLines(snapshot_check_hint())
Output
* Run `testthat::snapshot_download_gh("r-lib/testthat", "123")` to download snapshots.
* Run `testthat::snapshot_accept()` to accept all changes.
* Run `testthat::snapshot_review()` to review all changes.

32 changes: 3 additions & 29 deletions tests/testthat/_snaps/snapshot-file.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,38 +22,12 @@
Error in `snapshot_file_equal_()`:
! 'doesnt-exist.txt' not found.

# snapshot_hint output differs in R CMD check
# generates informative hint

Code
cat(snapshot_review_hint("lala", "foo.r", check = FALSE, ci = FALSE))
cat(snapshot_review_hint("lala", "foo.r", reset_output = FALSE))
Output
* Run `testthat::snapshot_review('lala/')` to review changes

---

Code
cat(snapshot_review_hint("lala", "foo.r", check = TRUE, ci = FALSE))
Output
* Locate check directory
* Copy 'tests/testthat/_snaps/lala/foo.new.r' to local test directory
* Run `testthat::snapshot_review('lala/')` to review changes

---

Code
cat(snapshot_review_hint("lala", "foo.r", check = TRUE, ci = TRUE))
Output
* Download and unzip run artifact
* Copy 'tests/testthat/_snaps/lala/foo.new.r' to local test directory
* Run `testthat::snapshot_review('lala/')` to review changes

---

Code
cat(snapshot_review_hint("lala", "foo.r", check = TRUE, ci = TRUE))
Output
* Call `snapshot_download_gh("r-lib/testthat", "123")` to download the snapshots from GitHub.
* Run `testthat::snapshot_review('lala/')` to review changes
* Run `testthat::snapshot_review('lala/foo.r')` to review the change.

# expect_snapshot_file validates its inputs

Expand Down
Loading
Loading