From b1d45018cfd37df99c6005711356fa07c4be8507 Mon Sep 17 00:00:00 2001 From: Hadley Wickham Date: Mon, 4 Aug 2025 13:01:57 -0500 Subject: [PATCH 1/5] Slow test reporter Initial prototype by claude, but lots of iteration by me. Fixes #1466 --- CLAUDE.md | 5 +- NAMESPACE | 1 + R/reporter-timing.R | 106 ++++++++++++++++++++++++++++++++++++++++ man/CheckReporter.Rd | 3 +- man/DebugReporter.Rd | 3 +- man/FailReporter.Rd | 3 +- man/JunitReporter.Rd | 3 +- man/ListReporter.Rd | 3 +- man/LocationReporter.Rd | 3 +- man/MinimalReporter.Rd | 3 +- man/MultiReporter.Rd | 3 +- man/ProgressReporter.Rd | 3 +- man/RStudioReporter.Rd | 3 +- man/Reporter.Rd | 3 +- man/SilentReporter.Rd | 3 +- man/StopReporter.Rd | 3 +- man/SummaryReporter.Rd | 3 +- man/TapReporter.Rd | 3 +- man/TeamcityReporter.Rd | 3 +- man/TimingReporter.Rd | 32 ++++++++++++ 20 files changed, 175 insertions(+), 17 deletions(-) create mode 100644 R/reporter-timing.R create mode 100644 man/TimingReporter.Rd diff --git a/CLAUDE.md b/CLAUDE.md index bf20bcbaf..7a2a7cd74 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -17,10 +17,13 @@ General advice: - `devtools::test()` - Run all tests - `devtools::test_file("tests/testthat/test-filename.R")` - Run tests in a specific file - `devtools::load_all()` - Load package for development -- `devtools::document()` - Generate documentation - `devtools::check()` - Run R CMD check - `devtools::install()` - Install package locally +### Documentation + +- Always run `devtools::document()` after changing any roxygen2 docs. + ## Core Architecture ### Main Components diff --git a/NAMESPACE b/NAMESPACE index 1d06507d0..f51fa1e45 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -49,6 +49,7 @@ export(StopReporter) export(SummaryReporter) export(TapReporter) export(TeamcityReporter) +export(TimingReporter) export(announce_snapshot_file) export(auto_test) export(auto_test_package) diff --git a/R/reporter-timing.R b/R/reporter-timing.R new file mode 100644 index 000000000..3cbbf96c1 --- /dev/null +++ b/R/reporter-timing.R @@ -0,0 +1,106 @@ +#' Test reporter: show timings for slow tests +#' +#' @description +#' `TimingReporter` is designed to identify slow tests. It reports the +#' execution time for each test and can optionally filter out tests that +#' run faster than a specified threshold (default: 1 second). This reporter +#' is useful for performance optimization and identifying tests that may +#' benefit from optimization or parallelization. +#' +#' @export +#' @family reporters +TimingReporter <- R6::R6Class( + "TimingReporter", + inherit = Reporter, + public = list( + min_time = 0.5, + test_timings = NULL, + current_test_start = NULL, + current_file = NULL, + + initialize = function(min_time = 0.5, ...) { + super$initialize(...) + self$min_time <- min_time + self$test_timings <- list() + }, + + start_reporter = function(context) { + self$cat_line( + cli::style_bold("Slow tests"), + " (showing tests >= ", + self$min_time, + "s)" + ) + self$cat_line() + }, + + start_file = function(file) { + self$current_file <- file + }, + + start_test = function(context, test) { + self$current_test_start <- proc.time()[[3]] + }, + + end_test = function(context, test) { + if (is.null(self$current_test_start)) { + return() + } + + time_taken <- proc.time()[[3]] - self$current_test_start + + # Store timing information + timing <- list( + file = self$current_file, + test = test, + time = time_taken + ) + self$test_timings[[length(self$test_timings) + 1]] <- timing + + if (time_taken >= self$min_time) { + self$show_timing(timing) + } + + self$current_test_start <- NULL + }, + + end_reporter = function() { + if (length(self$test_timings) == 0) { + return() + } + + all_times <- map_dbl(self$test_timings, \(x) x$time) + is_slow <- all_times >= self$min_time + + self$cat_line() + self$rule(cli::style_bold("Summary"), line = 2) + self$cat_line("All tests: ", sprintf("%.2fs", sum(all_times))) + self$cat_line("Slow tests: ", sprintf("%.2fs", sum(all_times[is_slow]))) + + if (sum(is_slow) <= 10) { + return() + } + + # Sort by time descending for summary + slowest <- self$test_timings[order(all_times, decreasing = TRUE)] + + self$cat_line() + self$rule(cli::style_bold("Slowest tests:"), line = 1) + + # Show top 10 slowest tests + for (i in 1:10) { + self$show_timing(slowest[[i]]) + } + + if (length(slowest) > 10) { + self$cat_line("... and ", length(slowest) - 10, " more slow tests") + } + + self$cat_line() + }, + show_timing = function(timing) { + time <- sprintf("%.2fs", timing$time) + self$cat_line("[", time, "] ", time, " ", timing$file, ": ", timing$test) + } + ) +) diff --git a/man/CheckReporter.Rd b/man/CheckReporter.Rd index a32c58c3d..2dfc376eb 100644 --- a/man/CheckReporter.Rd +++ b/man/CheckReporter.Rd @@ -23,6 +23,7 @@ Other reporters: \code{\link{StopReporter}}, \code{\link{SummaryReporter}}, \code{\link{TapReporter}}, -\code{\link{TeamcityReporter}} +\code{\link{TeamcityReporter}}, +\code{\link{TimingReporter}} } \concept{reporters} diff --git a/man/DebugReporter.Rd b/man/DebugReporter.Rd index 2ad10ba08..64a6deb4a 100644 --- a/man/DebugReporter.Rd +++ b/man/DebugReporter.Rd @@ -23,6 +23,7 @@ Other reporters: \code{\link{StopReporter}}, \code{\link{SummaryReporter}}, \code{\link{TapReporter}}, -\code{\link{TeamcityReporter}} +\code{\link{TeamcityReporter}}, +\code{\link{TimingReporter}} } \concept{reporters} diff --git a/man/FailReporter.Rd b/man/FailReporter.Rd index 1b9d0d2a7..008c1923b 100644 --- a/man/FailReporter.Rd +++ b/man/FailReporter.Rd @@ -24,6 +24,7 @@ Other reporters: \code{\link{StopReporter}}, \code{\link{SummaryReporter}}, \code{\link{TapReporter}}, -\code{\link{TeamcityReporter}} +\code{\link{TeamcityReporter}}, +\code{\link{TimingReporter}} } \concept{reporters} diff --git a/man/JunitReporter.Rd b/man/JunitReporter.Rd index c822771ac..bc8c24375 100644 --- a/man/JunitReporter.Rd +++ b/man/JunitReporter.Rd @@ -38,6 +38,7 @@ Other reporters: \code{\link{StopReporter}}, \code{\link{SummaryReporter}}, \code{\link{TapReporter}}, -\code{\link{TeamcityReporter}} +\code{\link{TeamcityReporter}}, +\code{\link{TimingReporter}} } \concept{reporters} diff --git a/man/ListReporter.Rd b/man/ListReporter.Rd index dbcb62f77..e423ead43 100644 --- a/man/ListReporter.Rd +++ b/man/ListReporter.Rd @@ -24,6 +24,7 @@ Other reporters: \code{\link{StopReporter}}, \code{\link{SummaryReporter}}, \code{\link{TapReporter}}, -\code{\link{TeamcityReporter}} +\code{\link{TeamcityReporter}}, +\code{\link{TimingReporter}} } \concept{reporters} diff --git a/man/LocationReporter.Rd b/man/LocationReporter.Rd index f0b5d86ca..0ba645ff5 100644 --- a/man/LocationReporter.Rd +++ b/man/LocationReporter.Rd @@ -24,6 +24,7 @@ Other reporters: \code{\link{StopReporter}}, \code{\link{SummaryReporter}}, \code{\link{TapReporter}}, -\code{\link{TeamcityReporter}} +\code{\link{TeamcityReporter}}, +\code{\link{TimingReporter}} } \concept{reporters} diff --git a/man/MinimalReporter.Rd b/man/MinimalReporter.Rd index 5365d7e46..f214c6fc6 100644 --- a/man/MinimalReporter.Rd +++ b/man/MinimalReporter.Rd @@ -25,6 +25,7 @@ Other reporters: \code{\link{StopReporter}}, \code{\link{SummaryReporter}}, \code{\link{TapReporter}}, -\code{\link{TeamcityReporter}} +\code{\link{TeamcityReporter}}, +\code{\link{TimingReporter}} } \concept{reporters} diff --git a/man/MultiReporter.Rd b/man/MultiReporter.Rd index bb5d9cb34..398cd0466 100644 --- a/man/MultiReporter.Rd +++ b/man/MultiReporter.Rd @@ -23,6 +23,7 @@ Other reporters: \code{\link{StopReporter}}, \code{\link{SummaryReporter}}, \code{\link{TapReporter}}, -\code{\link{TeamcityReporter}} +\code{\link{TeamcityReporter}}, +\code{\link{TimingReporter}} } \concept{reporters} diff --git a/man/ProgressReporter.Rd b/man/ProgressReporter.Rd index be63cf3b4..6777218d0 100644 --- a/man/ProgressReporter.Rd +++ b/man/ProgressReporter.Rd @@ -34,6 +34,7 @@ Other reporters: \code{\link{StopReporter}}, \code{\link{SummaryReporter}}, \code{\link{TapReporter}}, -\code{\link{TeamcityReporter}} +\code{\link{TeamcityReporter}}, +\code{\link{TimingReporter}} } \concept{reporters} diff --git a/man/RStudioReporter.Rd b/man/RStudioReporter.Rd index a25c39f9d..ad8777354 100644 --- a/man/RStudioReporter.Rd +++ b/man/RStudioReporter.Rd @@ -23,6 +23,7 @@ Other reporters: \code{\link{StopReporter}}, \code{\link{SummaryReporter}}, \code{\link{TapReporter}}, -\code{\link{TeamcityReporter}} +\code{\link{TeamcityReporter}}, +\code{\link{TimingReporter}} } \concept{reporters} diff --git a/man/Reporter.Rd b/man/Reporter.Rd index eeb7a0ab2..a6def5b42 100644 --- a/man/Reporter.Rd +++ b/man/Reporter.Rd @@ -39,7 +39,8 @@ Other reporters: \code{\link{StopReporter}}, \code{\link{SummaryReporter}}, \code{\link{TapReporter}}, -\code{\link{TeamcityReporter}} +\code{\link{TeamcityReporter}}, +\code{\link{TimingReporter}} } \concept{reporters} \keyword{internal} diff --git a/man/SilentReporter.Rd b/man/SilentReporter.Rd index dde2f9641..0dd0c4945 100644 --- a/man/SilentReporter.Rd +++ b/man/SilentReporter.Rd @@ -25,6 +25,7 @@ Other reporters: \code{\link{StopReporter}}, \code{\link{SummaryReporter}}, \code{\link{TapReporter}}, -\code{\link{TeamcityReporter}} +\code{\link{TeamcityReporter}}, +\code{\link{TimingReporter}} } \concept{reporters} diff --git a/man/StopReporter.Rd b/man/StopReporter.Rd index 53d5f41f5..10d8203bc 100644 --- a/man/StopReporter.Rd +++ b/man/StopReporter.Rd @@ -29,6 +29,7 @@ Other reporters: \code{\link{SilentReporter}}, \code{\link{SummaryReporter}}, \code{\link{TapReporter}}, -\code{\link{TeamcityReporter}} +\code{\link{TeamcityReporter}}, +\code{\link{TimingReporter}} } \concept{reporters} diff --git a/man/SummaryReporter.Rd b/man/SummaryReporter.Rd index 149182127..787ddc642 100644 --- a/man/SummaryReporter.Rd +++ b/man/SummaryReporter.Rd @@ -32,6 +32,7 @@ Other reporters: \code{\link{SilentReporter}}, \code{\link{StopReporter}}, \code{\link{TapReporter}}, -\code{\link{TeamcityReporter}} +\code{\link{TeamcityReporter}}, +\code{\link{TimingReporter}} } \concept{reporters} diff --git a/man/TapReporter.Rd b/man/TapReporter.Rd index 6c8a680e0..ed1bdbefc 100644 --- a/man/TapReporter.Rd +++ b/man/TapReporter.Rd @@ -24,6 +24,7 @@ Other reporters: \code{\link{SilentReporter}}, \code{\link{StopReporter}}, \code{\link{SummaryReporter}}, -\code{\link{TeamcityReporter}} +\code{\link{TeamcityReporter}}, +\code{\link{TimingReporter}} } \concept{reporters} diff --git a/man/TeamcityReporter.Rd b/man/TeamcityReporter.Rd index f2b2b79de..4041e6bbc 100644 --- a/man/TeamcityReporter.Rd +++ b/man/TeamcityReporter.Rd @@ -24,6 +24,7 @@ Other reporters: \code{\link{SilentReporter}}, \code{\link{StopReporter}}, \code{\link{SummaryReporter}}, -\code{\link{TapReporter}} +\code{\link{TapReporter}}, +\code{\link{TimingReporter}} } \concept{reporters} diff --git a/man/TimingReporter.Rd b/man/TimingReporter.Rd new file mode 100644 index 000000000..508178ca2 --- /dev/null +++ b/man/TimingReporter.Rd @@ -0,0 +1,32 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/reporter-timing.R +\name{TimingReporter} +\alias{TimingReporter} +\title{Test reporter: show timings for slow tests} +\description{ +\code{TimingReporter} is designed to identify slow tests. It reports the +execution time for each test and can optionally filter out tests that +run faster than a specified threshold (default: 1 second). This reporter +is useful for performance optimization and identifying tests that may +benefit from optimization or parallelization. +} +\seealso{ +Other reporters: +\code{\link{CheckReporter}}, +\code{\link{DebugReporter}}, +\code{\link{FailReporter}}, +\code{\link{JunitReporter}}, +\code{\link{ListReporter}}, +\code{\link{LocationReporter}}, +\code{\link{MinimalReporter}}, +\code{\link{MultiReporter}}, +\code{\link{ProgressReporter}}, +\code{\link{RStudioReporter}}, +\code{\link{Reporter}}, +\code{\link{SilentReporter}}, +\code{\link{StopReporter}}, +\code{\link{SummaryReporter}}, +\code{\link{TapReporter}}, +\code{\link{TeamcityReporter}} +} +\concept{reporters} From 719e3f81d109c4e07eff0db8b3bb6c21f2335599 Mon Sep 17 00:00:00 2001 From: Hadley Wickham Date: Tue, 5 Aug 2025 11:27:01 -0500 Subject: [PATCH 2/5] Rename + polish --- NAMESPACE | 2 +- R/reporter-slow.R | 106 ++++++++++++++++++++++++++++++++++++++++ R/reporter-timing.R | 31 +++++------- man/CheckReporter.Rd | 4 +- man/DebugReporter.Rd | 4 +- man/FailReporter.Rd | 4 +- man/JunitReporter.Rd | 4 +- man/ListReporter.Rd | 4 +- man/LocationReporter.Rd | 4 +- man/MinimalReporter.Rd | 4 +- man/MultiReporter.Rd | 4 +- man/ProgressReporter.Rd | 4 +- man/RStudioReporter.Rd | 4 +- man/Reporter.Rd | 4 +- man/SilentReporter.Rd | 4 +- man/SlowReporter.Rd | 56 +++++++++++++++++++++ man/StopReporter.Rd | 4 +- man/SummaryReporter.Rd | 4 +- man/TapReporter.Rd | 4 +- man/TeamcityReporter.Rd | 4 +- man/TimingReporter.Rd | 32 ------------ 21 files changed, 207 insertions(+), 84 deletions(-) create mode 100644 R/reporter-slow.R create mode 100644 man/SlowReporter.Rd delete mode 100644 man/TimingReporter.Rd diff --git a/NAMESPACE b/NAMESPACE index f51fa1e45..ee6dcd8be 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -45,11 +45,11 @@ export(ProgressReporter) export(RStudioReporter) export(Reporter) export(SilentReporter) +export(SlowReporter) export(StopReporter) export(SummaryReporter) export(TapReporter) export(TeamcityReporter) -export(TimingReporter) export(announce_snapshot_file) export(auto_test) export(auto_test_package) diff --git a/R/reporter-slow.R b/R/reporter-slow.R new file mode 100644 index 000000000..26ea10c6a --- /dev/null +++ b/R/reporter-slow.R @@ -0,0 +1,106 @@ +#' Test reporter: show timings for slow tests +#' +#' @description +#' `SlowReporter` is designed to identify slow tests. It reports the +#' execution time for each test and can optionally filter out tests that +#' run faster than a specified threshold (default: 1 second). This reporter +#' is useful for performance optimization and identifying tests that may +#' benefit from optimization or parallelization. +#' +#' @export +#' @family reporters +SlowReporter <- R6::R6Class( + "SlowReporter", + inherit = Reporter, + public = list( + min_time = 0.5, + test_timings = NULL, + current_test_start = NULL, + current_file = NULL, + + initialize = function(min_time = 0.5, ...) { + super$initialize(...) + self$min_time <- min_time + self$test_timings <- list() + }, + + start_reporter = function(context) { + self$cat_line( + cli::style_bold("Slow tests"), + " (showing tests >= ", + self$min_time, + "s)" + ) + self$cat_line() + }, + + start_file = function(file) { + self$current_file <- file + }, + + start_test = function(context, test) { + self$current_test_start <- proc.time()[[3]] + }, + + end_test = function(context, test) { + if (is.null(self$current_test_start)) { + return() + } + + time_taken <- proc.time()[[3]] - self$current_test_start + + # Store timing information + timing <- list( + file = self$current_file, + test = test, + time = time_taken + ) + self$test_timings[[length(self$test_timings) + 1]] <- timing + + if (time_taken >= self$min_time) { + self$show_timing(timing) + } + + self$current_test_start <- NULL + }, + + end_reporter = function() { + if (length(self$test_timings) == 0) { + return() + } + + all_times <- map_dbl(self$test_timings, \(x) x$time) + is_slow <- all_times >= self$min_time + + self$cat_line() + self$rule(cli::style_bold("Summary"), line = 2) + self$cat_line("All tests: ", sprintf("%.2fs", sum(all_times))) + self$cat_line("Slow tests: ", sprintf("%.2fs", sum(all_times[is_slow]))) + + if (sum(is_slow) <= 10) { + return() + } + + # Sort by time descending for summary + slowest <- self$test_timings[order(all_times, decreasing = TRUE)] + + self$cat_line() + self$rule(cli::style_bold("Slowest tests:"), line = 1) + + # Show top 10 slowest tests + for (i in 1:10) { + self$show_timing(slowest[[i]]) + } + + if (length(slowest) > 10) { + self$cat_line("... and ", length(slowest) - 10, " more slow tests") + } + + self$cat_line() + }, + show_timing = function(timing) { + time <- sprintf("%.2fs", timing$time) + self$cat_line("[", time, "] ", time, " ", timing$file, ": ", timing$test) + } + ) +) diff --git a/R/reporter-timing.R b/R/reporter-timing.R index 3cbbf96c1..84a89633e 100644 --- a/R/reporter-timing.R +++ b/R/reporter-timing.R @@ -1,39 +1,32 @@ #' Test reporter: show timings for slow tests #' #' @description -#' `TimingReporter` is designed to identify slow tests. It reports the -#' execution time for each test and can optionally filter out tests that -#' run faster than a specified threshold (default: 1 second). This reporter -#' is useful for performance optimization and identifying tests that may -#' benefit from optimization or parallelization. +#' `SlowReporter` is designed to identify slow tests. It reports the +#' execution time for each test, ignoring tests faster than a specified +#' threshold (default: 0.5s). +#' +#' The easiest way to run it over your package is with +#' `devtools::test(reporter = "slow")`. #' #' @export #' @family reporters -TimingReporter <- R6::R6Class( - "TimingReporter", +SlowReporter <- R6::R6Class( + "SlowReporter", inherit = Reporter, public = list( - min_time = 0.5, + min_time = NA_real_, test_timings = NULL, current_test_start = NULL, current_file = NULL, initialize = function(min_time = 0.5, ...) { + check_number_decimal(min_time, min = 0) + super$initialize(...) self$min_time <- min_time self$test_timings <- list() }, - start_reporter = function(context) { - self$cat_line( - cli::style_bold("Slow tests"), - " (showing tests >= ", - self$min_time, - "s)" - ) - self$cat_line() - }, - start_file = function(file) { self$current_file <- file }, @@ -100,7 +93,7 @@ TimingReporter <- R6::R6Class( }, show_timing = function(timing) { time <- sprintf("%.2fs", timing$time) - self$cat_line("[", time, "] ", time, " ", timing$file, ": ", timing$test) + self$cat_line("[", time, "] ", timing$file, ": ", timing$test) } ) ) diff --git a/man/CheckReporter.Rd b/man/CheckReporter.Rd index 2dfc376eb..872f5c213 100644 --- a/man/CheckReporter.Rd +++ b/man/CheckReporter.Rd @@ -20,10 +20,10 @@ Other reporters: \code{\link{RStudioReporter}}, \code{\link{Reporter}}, \code{\link{SilentReporter}}, +\code{\link{SlowReporter}}, \code{\link{StopReporter}}, \code{\link{SummaryReporter}}, \code{\link{TapReporter}}, -\code{\link{TeamcityReporter}}, -\code{\link{TimingReporter}} +\code{\link{TeamcityReporter}} } \concept{reporters} diff --git a/man/DebugReporter.Rd b/man/DebugReporter.Rd index 64a6deb4a..41693e314 100644 --- a/man/DebugReporter.Rd +++ b/man/DebugReporter.Rd @@ -20,10 +20,10 @@ Other reporters: \code{\link{RStudioReporter}}, \code{\link{Reporter}}, \code{\link{SilentReporter}}, +\code{\link{SlowReporter}}, \code{\link{StopReporter}}, \code{\link{SummaryReporter}}, \code{\link{TapReporter}}, -\code{\link{TeamcityReporter}}, -\code{\link{TimingReporter}} +\code{\link{TeamcityReporter}} } \concept{reporters} diff --git a/man/FailReporter.Rd b/man/FailReporter.Rd index 008c1923b..2728512f6 100644 --- a/man/FailReporter.Rd +++ b/man/FailReporter.Rd @@ -21,10 +21,10 @@ Other reporters: \code{\link{RStudioReporter}}, \code{\link{Reporter}}, \code{\link{SilentReporter}}, +\code{\link{SlowReporter}}, \code{\link{StopReporter}}, \code{\link{SummaryReporter}}, \code{\link{TapReporter}}, -\code{\link{TeamcityReporter}}, -\code{\link{TimingReporter}} +\code{\link{TeamcityReporter}} } \concept{reporters} diff --git a/man/JunitReporter.Rd b/man/JunitReporter.Rd index bc8c24375..b89164afc 100644 --- a/man/JunitReporter.Rd +++ b/man/JunitReporter.Rd @@ -35,10 +35,10 @@ Other reporters: \code{\link{RStudioReporter}}, \code{\link{Reporter}}, \code{\link{SilentReporter}}, +\code{\link{SlowReporter}}, \code{\link{StopReporter}}, \code{\link{SummaryReporter}}, \code{\link{TapReporter}}, -\code{\link{TeamcityReporter}}, -\code{\link{TimingReporter}} +\code{\link{TeamcityReporter}} } \concept{reporters} diff --git a/man/ListReporter.Rd b/man/ListReporter.Rd index e423ead43..60c0cd29b 100644 --- a/man/ListReporter.Rd +++ b/man/ListReporter.Rd @@ -21,10 +21,10 @@ Other reporters: \code{\link{RStudioReporter}}, \code{\link{Reporter}}, \code{\link{SilentReporter}}, +\code{\link{SlowReporter}}, \code{\link{StopReporter}}, \code{\link{SummaryReporter}}, \code{\link{TapReporter}}, -\code{\link{TeamcityReporter}}, -\code{\link{TimingReporter}} +\code{\link{TeamcityReporter}} } \concept{reporters} diff --git a/man/LocationReporter.Rd b/man/LocationReporter.Rd index 0ba645ff5..494942357 100644 --- a/man/LocationReporter.Rd +++ b/man/LocationReporter.Rd @@ -21,10 +21,10 @@ Other reporters: \code{\link{RStudioReporter}}, \code{\link{Reporter}}, \code{\link{SilentReporter}}, +\code{\link{SlowReporter}}, \code{\link{StopReporter}}, \code{\link{SummaryReporter}}, \code{\link{TapReporter}}, -\code{\link{TeamcityReporter}}, -\code{\link{TimingReporter}} +\code{\link{TeamcityReporter}} } \concept{reporters} diff --git a/man/MinimalReporter.Rd b/man/MinimalReporter.Rd index f214c6fc6..26561b621 100644 --- a/man/MinimalReporter.Rd +++ b/man/MinimalReporter.Rd @@ -22,10 +22,10 @@ Other reporters: \code{\link{RStudioReporter}}, \code{\link{Reporter}}, \code{\link{SilentReporter}}, +\code{\link{SlowReporter}}, \code{\link{StopReporter}}, \code{\link{SummaryReporter}}, \code{\link{TapReporter}}, -\code{\link{TeamcityReporter}}, -\code{\link{TimingReporter}} +\code{\link{TeamcityReporter}} } \concept{reporters} diff --git a/man/MultiReporter.Rd b/man/MultiReporter.Rd index 398cd0466..f35575b6b 100644 --- a/man/MultiReporter.Rd +++ b/man/MultiReporter.Rd @@ -20,10 +20,10 @@ Other reporters: \code{\link{RStudioReporter}}, \code{\link{Reporter}}, \code{\link{SilentReporter}}, +\code{\link{SlowReporter}}, \code{\link{StopReporter}}, \code{\link{SummaryReporter}}, \code{\link{TapReporter}}, -\code{\link{TeamcityReporter}}, -\code{\link{TimingReporter}} +\code{\link{TeamcityReporter}} } \concept{reporters} diff --git a/man/ProgressReporter.Rd b/man/ProgressReporter.Rd index 6777218d0..6a1ba4ba8 100644 --- a/man/ProgressReporter.Rd +++ b/man/ProgressReporter.Rd @@ -31,10 +31,10 @@ Other reporters: \code{\link{RStudioReporter}}, \code{\link{Reporter}}, \code{\link{SilentReporter}}, +\code{\link{SlowReporter}}, \code{\link{StopReporter}}, \code{\link{SummaryReporter}}, \code{\link{TapReporter}}, -\code{\link{TeamcityReporter}}, -\code{\link{TimingReporter}} +\code{\link{TeamcityReporter}} } \concept{reporters} diff --git a/man/RStudioReporter.Rd b/man/RStudioReporter.Rd index ad8777354..9a8e8ffcf 100644 --- a/man/RStudioReporter.Rd +++ b/man/RStudioReporter.Rd @@ -20,10 +20,10 @@ Other reporters: \code{\link{ProgressReporter}}, \code{\link{Reporter}}, \code{\link{SilentReporter}}, +\code{\link{SlowReporter}}, \code{\link{StopReporter}}, \code{\link{SummaryReporter}}, \code{\link{TapReporter}}, -\code{\link{TeamcityReporter}}, -\code{\link{TimingReporter}} +\code{\link{TeamcityReporter}} } \concept{reporters} diff --git a/man/Reporter.Rd b/man/Reporter.Rd index a6def5b42..891c58987 100644 --- a/man/Reporter.Rd +++ b/man/Reporter.Rd @@ -36,11 +36,11 @@ Other reporters: \code{\link{ProgressReporter}}, \code{\link{RStudioReporter}}, \code{\link{SilentReporter}}, +\code{\link{SlowReporter}}, \code{\link{StopReporter}}, \code{\link{SummaryReporter}}, \code{\link{TapReporter}}, -\code{\link{TeamcityReporter}}, -\code{\link{TimingReporter}} +\code{\link{TeamcityReporter}} } \concept{reporters} \keyword{internal} diff --git a/man/SilentReporter.Rd b/man/SilentReporter.Rd index 0dd0c4945..5da9f8b82 100644 --- a/man/SilentReporter.Rd +++ b/man/SilentReporter.Rd @@ -22,10 +22,10 @@ Other reporters: \code{\link{ProgressReporter}}, \code{\link{RStudioReporter}}, \code{\link{Reporter}}, +\code{\link{SlowReporter}}, \code{\link{StopReporter}}, \code{\link{SummaryReporter}}, \code{\link{TapReporter}}, -\code{\link{TeamcityReporter}}, -\code{\link{TimingReporter}} +\code{\link{TeamcityReporter}} } \concept{reporters} diff --git a/man/SlowReporter.Rd b/man/SlowReporter.Rd new file mode 100644 index 000000000..3395100f1 --- /dev/null +++ b/man/SlowReporter.Rd @@ -0,0 +1,56 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/reporter-slow.R, R/reporter-timing.R +\name{SlowReporter} +\alias{SlowReporter} +\title{Test reporter: show timings for slow tests} +\description{ +\code{SlowReporter} is designed to identify slow tests. It reports the +execution time for each test and can optionally filter out tests that +run faster than a specified threshold (default: 1 second). This reporter +is useful for performance optimization and identifying tests that may +benefit from optimization or parallelization. + +\code{SlowReporter} is designed to identify slow tests. It reports the +execution time for each test and can optionally filter out tests that +run faster than a specified threshold (default: 1 second). This reporter +is useful for performance optimization and identifying tests that may +benefit from optimization or parallelization. +} +\seealso{ +Other reporters: +\code{\link{CheckReporter}}, +\code{\link{DebugReporter}}, +\code{\link{FailReporter}}, +\code{\link{JunitReporter}}, +\code{\link{ListReporter}}, +\code{\link{LocationReporter}}, +\code{\link{MinimalReporter}}, +\code{\link{MultiReporter}}, +\code{\link{ProgressReporter}}, +\code{\link{RStudioReporter}}, +\code{\link{Reporter}}, +\code{\link{SilentReporter}}, +\code{\link{StopReporter}}, +\code{\link{SummaryReporter}}, +\code{\link{TapReporter}}, +\code{\link{TeamcityReporter}} + +Other reporters: +\code{\link{CheckReporter}}, +\code{\link{DebugReporter}}, +\code{\link{FailReporter}}, +\code{\link{JunitReporter}}, +\code{\link{ListReporter}}, +\code{\link{LocationReporter}}, +\code{\link{MinimalReporter}}, +\code{\link{MultiReporter}}, +\code{\link{ProgressReporter}}, +\code{\link{RStudioReporter}}, +\code{\link{Reporter}}, +\code{\link{SilentReporter}}, +\code{\link{StopReporter}}, +\code{\link{SummaryReporter}}, +\code{\link{TapReporter}}, +\code{\link{TeamcityReporter}} +} +\concept{reporters} diff --git a/man/StopReporter.Rd b/man/StopReporter.Rd index 10d8203bc..3d5850935 100644 --- a/man/StopReporter.Rd +++ b/man/StopReporter.Rd @@ -27,9 +27,9 @@ Other reporters: \code{\link{RStudioReporter}}, \code{\link{Reporter}}, \code{\link{SilentReporter}}, +\code{\link{SlowReporter}}, \code{\link{SummaryReporter}}, \code{\link{TapReporter}}, -\code{\link{TeamcityReporter}}, -\code{\link{TimingReporter}} +\code{\link{TeamcityReporter}} } \concept{reporters} diff --git a/man/SummaryReporter.Rd b/man/SummaryReporter.Rd index 787ddc642..0ebbe2e66 100644 --- a/man/SummaryReporter.Rd +++ b/man/SummaryReporter.Rd @@ -30,9 +30,9 @@ Other reporters: \code{\link{RStudioReporter}}, \code{\link{Reporter}}, \code{\link{SilentReporter}}, +\code{\link{SlowReporter}}, \code{\link{StopReporter}}, \code{\link{TapReporter}}, -\code{\link{TeamcityReporter}}, -\code{\link{TimingReporter}} +\code{\link{TeamcityReporter}} } \concept{reporters} diff --git a/man/TapReporter.Rd b/man/TapReporter.Rd index ed1bdbefc..1ac66f545 100644 --- a/man/TapReporter.Rd +++ b/man/TapReporter.Rd @@ -22,9 +22,9 @@ Other reporters: \code{\link{RStudioReporter}}, \code{\link{Reporter}}, \code{\link{SilentReporter}}, +\code{\link{SlowReporter}}, \code{\link{StopReporter}}, \code{\link{SummaryReporter}}, -\code{\link{TeamcityReporter}}, -\code{\link{TimingReporter}} +\code{\link{TeamcityReporter}} } \concept{reporters} diff --git a/man/TeamcityReporter.Rd b/man/TeamcityReporter.Rd index 4041e6bbc..088fcad79 100644 --- a/man/TeamcityReporter.Rd +++ b/man/TeamcityReporter.Rd @@ -22,9 +22,9 @@ Other reporters: \code{\link{RStudioReporter}}, \code{\link{Reporter}}, \code{\link{SilentReporter}}, +\code{\link{SlowReporter}}, \code{\link{StopReporter}}, \code{\link{SummaryReporter}}, -\code{\link{TapReporter}}, -\code{\link{TimingReporter}} +\code{\link{TapReporter}} } \concept{reporters} diff --git a/man/TimingReporter.Rd b/man/TimingReporter.Rd deleted file mode 100644 index 508178ca2..000000000 --- a/man/TimingReporter.Rd +++ /dev/null @@ -1,32 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/reporter-timing.R -\name{TimingReporter} -\alias{TimingReporter} -\title{Test reporter: show timings for slow tests} -\description{ -\code{TimingReporter} is designed to identify slow tests. It reports the -execution time for each test and can optionally filter out tests that -run faster than a specified threshold (default: 1 second). This reporter -is useful for performance optimization and identifying tests that may -benefit from optimization or parallelization. -} -\seealso{ -Other reporters: -\code{\link{CheckReporter}}, -\code{\link{DebugReporter}}, -\code{\link{FailReporter}}, -\code{\link{JunitReporter}}, -\code{\link{ListReporter}}, -\code{\link{LocationReporter}}, -\code{\link{MinimalReporter}}, -\code{\link{MultiReporter}}, -\code{\link{ProgressReporter}}, -\code{\link{RStudioReporter}}, -\code{\link{Reporter}}, -\code{\link{SilentReporter}}, -\code{\link{StopReporter}}, -\code{\link{SummaryReporter}}, -\code{\link{TapReporter}}, -\code{\link{TeamcityReporter}} -} -\concept{reporters} From ed90aa25507336629b6bf197b7c9d077e4de2c6c Mon Sep 17 00:00:00 2001 From: Hadley Wickham Date: Tue, 5 Aug 2025 11:27:14 -0500 Subject: [PATCH 3/5] Re-doc --- man/SlowReporter.Rd | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/man/SlowReporter.Rd b/man/SlowReporter.Rd index 3395100f1..4897e6e36 100644 --- a/man/SlowReporter.Rd +++ b/man/SlowReporter.Rd @@ -11,10 +11,11 @@ is useful for performance optimization and identifying tests that may benefit from optimization or parallelization. \code{SlowReporter} is designed to identify slow tests. It reports the -execution time for each test and can optionally filter out tests that -run faster than a specified threshold (default: 1 second). This reporter -is useful for performance optimization and identifying tests that may -benefit from optimization or parallelization. +execution time for each test, ignoring tests faster than a specified +threshold (default: 0.5s). + +The easiest way to run it over your package is with +\code{devtools::test(reporter = "slow")}. } \seealso{ Other reporters: From 5c69b6816a4375c807cdbaac4dcbf4e66d227192 Mon Sep 17 00:00:00 2001 From: Hadley Wickham Date: Tue, 5 Aug 2025 11:28:15 -0500 Subject: [PATCH 4/5] Add news bullet --- NEWS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/NEWS.md b/NEWS.md index c7e7e85d9..0ab1d03dc 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,6 @@ # testthat (development version) +* New `SlowReporter` makes it easier to find the slowest tests in your package. The easiest way to run it is with `devtools::test(reporter = "slow")` (#1466). * Parallel testthat now does not ignore test files with syntax errors (#1360). * `expect_lt()`, `expect_gt()`, and friends have a refined display that is more likely to display the correct number of digits and shows you the actual values compared. * `describe()`, `it()`, and `test_that()` now have a shared stack of descriptions so that if you nest any inside of each other, any resulting failures will show you the full path. From e238b006506c00ea1a0541a9cdb93ce9b12c003f Mon Sep 17 00:00:00 2001 From: Hadley Wickham Date: Tue, 5 Aug 2025 11:33:52 -0500 Subject: [PATCH 5/5] Add basic test --- tests/testthat/_snaps/reporter-slow.md | 21 +++++++++++++++++++++ tests/testthat/test-reporter-slow.R | 16 ++++++++++++++++ 2 files changed, 37 insertions(+) create mode 100644 tests/testthat/_snaps/reporter-slow.md create mode 100644 tests/testthat/test-reporter-slow.R diff --git a/tests/testthat/_snaps/reporter-slow.md b/tests/testthat/_snaps/reporter-slow.md new file mode 100644 index 000000000..a4e171c45 --- /dev/null +++ b/tests/testthat/_snaps/reporter-slow.md @@ -0,0 +1,21 @@ +# multiplication works + + Code + show_all <- SlowReporter$new(min_time = 0) + with_reporter(show_all, n_tests(10)) + Output + [-.--s] : run 1 + [-.--s] : run 2 + [-.--s] : run 3 + [-.--s] : run 4 + [-.--s] : run 5 + [-.--s] : run 6 + [-.--s] : run 7 + [-.--s] : run 8 + [-.--s] : run 9 + [-.--s] : run 10 + + == Summary ===================================================================== + All tests: -.--s + Slow tests: -.--s + diff --git a/tests/testthat/test-reporter-slow.R b/tests/testthat/test-reporter-slow.R new file mode 100644 index 000000000..64e2f63ec --- /dev/null +++ b/tests/testthat/test-reporter-slow.R @@ -0,0 +1,16 @@ +test_that("multiplication works", { + n_tests <- function(n) { + for (i in 1:n) { + test_that(paste0("run ", i), { + suceed() + }) + } + } + expect_snapshot( + { + show_all <- SlowReporter$new(min_time = 0) + with_reporter(show_all, n_tests(10)) + }, + transform = \(x) gsub("\\d.\\d\\ds", "-.--s", x), + ) +})