Skip to content

Commit b1d4501

Browse files
committed
Slow test reporter
Initial prototype by claude, but lots of iteration by me. Fixes #1466
1 parent 7b71880 commit b1d4501

20 files changed

+175
-17
lines changed

β€ŽCLAUDE.mdβ€Ž

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,13 @@ General advice:
1717
- `devtools::test()` - Run all tests
1818
- `devtools::test_file("tests/testthat/test-filename.R")` - Run tests in a specific file
1919
- `devtools::load_all()` - Load package for development
20-
- `devtools::document()` - Generate documentation
2120
- `devtools::check()` - Run R CMD check
2221
- `devtools::install()` - Install package locally
2322

23+
### Documentation
24+
25+
- Always run `devtools::document()` after changing any roxygen2 docs.
26+
2427
## Core Architecture
2528

2629
### Main Components

β€ŽNAMESPACEβ€Ž

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ export(StopReporter)
4949
export(SummaryReporter)
5050
export(TapReporter)
5151
export(TeamcityReporter)
52+
export(TimingReporter)
5253
export(announce_snapshot_file)
5354
export(auto_test)
5455
export(auto_test_package)

β€ŽR/reporter-timing.Rβ€Ž

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
#' Test reporter: show timings for slow tests
2+
#'
3+
#' @description
4+
#' `TimingReporter` is designed to identify slow tests. It reports the
5+
#' execution time for each test and can optionally filter out tests that
6+
#' run faster than a specified threshold (default: 1 second). This reporter
7+
#' is useful for performance optimization and identifying tests that may
8+
#' benefit from optimization or parallelization.
9+
#'
10+
#' @export
11+
#' @family reporters
12+
TimingReporter <- R6::R6Class(
13+
"TimingReporter",
14+
inherit = Reporter,
15+
public = list(
16+
min_time = 0.5,
17+
test_timings = NULL,
18+
current_test_start = NULL,
19+
current_file = NULL,
20+
21+
initialize = function(min_time = 0.5, ...) {
22+
super$initialize(...)
23+
self$min_time <- min_time
24+
self$test_timings <- list()
25+
},
26+
27+
start_reporter = function(context) {
28+
self$cat_line(
29+
cli::style_bold("Slow tests"),
30+
" (showing tests >= ",
31+
self$min_time,
32+
"s)"
33+
)
34+
self$cat_line()
35+
},
36+
37+
start_file = function(file) {
38+
self$current_file <- file
39+
},
40+
41+
start_test = function(context, test) {
42+
self$current_test_start <- proc.time()[[3]]
43+
},
44+
45+
end_test = function(context, test) {
46+
if (is.null(self$current_test_start)) {
47+
return()
48+
}
49+
50+
time_taken <- proc.time()[[3]] - self$current_test_start
51+
52+
# Store timing information
53+
timing <- list(
54+
file = self$current_file,
55+
test = test,
56+
time = time_taken
57+
)
58+
self$test_timings[[length(self$test_timings) + 1]] <- timing
59+
60+
if (time_taken >= self$min_time) {
61+
self$show_timing(timing)
62+
}
63+
64+
self$current_test_start <- NULL
65+
},
66+
67+
end_reporter = function() {
68+
if (length(self$test_timings) == 0) {
69+
return()
70+
}
71+
72+
all_times <- map_dbl(self$test_timings, \(x) x$time)
73+
is_slow <- all_times >= self$min_time
74+
75+
self$cat_line()
76+
self$rule(cli::style_bold("Summary"), line = 2)
77+
self$cat_line("All tests: ", sprintf("%.2fs", sum(all_times)))
78+
self$cat_line("Slow tests: ", sprintf("%.2fs", sum(all_times[is_slow])))
79+
80+
if (sum(is_slow) <= 10) {
81+
return()
82+
}
83+
84+
# Sort by time descending for summary
85+
slowest <- self$test_timings[order(all_times, decreasing = TRUE)]
86+
87+
self$cat_line()
88+
self$rule(cli::style_bold("Slowest tests:"), line = 1)
89+
90+
# Show top 10 slowest tests
91+
for (i in 1:10) {
92+
self$show_timing(slowest[[i]])
93+
}
94+
95+
if (length(slowest) > 10) {
96+
self$cat_line("... and ", length(slowest) - 10, " more slow tests")
97+
}
98+
99+
self$cat_line()
100+
},
101+
show_timing = function(timing) {
102+
time <- sprintf("%.2fs", timing$time)
103+
self$cat_line("[", time, "] ", time, " ", timing$file, ": ", timing$test)
104+
}
105+
)
106+
)

β€Žman/CheckReporter.Rdβ€Ž

Lines changed: 2 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

β€Žman/DebugReporter.Rdβ€Ž

Lines changed: 2 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

β€Žman/FailReporter.Rdβ€Ž

Lines changed: 2 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

β€Žman/JunitReporter.Rdβ€Ž

Lines changed: 2 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

β€Žman/ListReporter.Rdβ€Ž

Lines changed: 2 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

β€Žman/LocationReporter.Rdβ€Ž

Lines changed: 2 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

β€Žman/MinimalReporter.Rdβ€Ž

Lines changed: 2 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
Β (0)