Skip to content

Commit

Permalink
New ProgressReporter
Browse files Browse the repository at this point in the history
Fixes #529
  • Loading branch information
hadley committed Oct 4, 2017
1 parent 729bcd4 commit 317fa98
Show file tree
Hide file tree
Showing 22 changed files with 373 additions and 6 deletions.
4 changes: 3 additions & 1 deletion DESCRIPTION
Expand Up @@ -16,6 +16,7 @@ Encoding: UTF-8
Depends:
R (>= 3.1)
Imports:
clisymbols,
crayon,
digest,
magrittr,
Expand Down Expand Up @@ -67,10 +68,11 @@ Collate:
'reporter-location.R'
'reporter-minimal.R'
'reporter-multi.R'
'stack.R'
'reporter-progress.R'
'reporter-rstudio.R'
'reporter-silent.R'
'reporter-stop.R'
'stack.R'
'reporter-summary.R'
'reporter-tap.R'
'reporter-teamcity.R'
Expand Down
1 change: 1 addition & 0 deletions NAMESPACE
Expand Up @@ -30,6 +30,7 @@ export(ListReporter)
export(LocationReporter)
export(MinimalReporter)
export(MultiReporter)
export(ProgressReporter)
export(Reporter)
export(RstudioReporter)
export(SilentReporter)
Expand Down
5 changes: 5 additions & 0 deletions NEWS.md
@@ -1,5 +1,10 @@
# testthat 1.0.2.9000

* New default reporter `ReporterProgress` replaces the previous
`SummaryReporter`. It's a careful rethining of the default output that is
both more aesthetical pleasing and makes the most important information
available upfront (#529).

* `test_dir()` (and hence `test_package()`, and `test_check()`) now unsets
the `R_TESTS` env var (#603)

Expand Down
196 changes: 196 additions & 0 deletions R/reporter-progress.R
@@ -0,0 +1,196 @@
#' @include reporter.R stack.R
NULL

#' Test reporter: interactive progress bar of errors.
#'
#' This reporter is a reimagining of [SummaryReporter] desgined to make the
#' most information available up front, while taking up less space overall. It
#' is the default reporting reporter used by [test_dir()] and [test_file()].
#'
#' As an additional benefit, this reporter will praise you from time-to-time
#' if all your tests pass.
#'
#' @export
#' @family reporters
ProgressReporter <- R6::R6Class("ProgressReporter", inherit = Reporter,
public = list(
failures = NULL,
skips = NULL,
warnings = NULL,
show_praise = TRUE,

max_failures = NULL,
n_ok = 0,
n_skip = 0,
n_warn = 0,
n_fail = 0,

ctxt_start_time = NULL,
ctxt_issues = NULL,
ctxt_n = 0,
ctxt_n_ok = 0,
ctxt_n_skip = 0,
ctxt_n_warn = 0,
ctxt_n_fail = 0,
ctxt_name = 0,

initialize = function(show_praise = TRUE,
max_failures = getOption("testthat.progress.max_fails", 10L)
) {
super$initialize()
self$max_failures <- max_failures
self$show_praise <- show_praise
},

start_reporter = function(context) {
self$show_header()
},

start_context = function(context) {
self$ctxt_name <- context
self$ctxt_issues <- Stack$new()

self$ctxt_n <- 0L
self$ctxt_n_ok <- 0L
self$ctxt_n_fail <- 0L
self$ctxt_n_warn <- 0L
self$ctxt_n_skip <- 0L

self$ctxt_start_time <- proc.time()
self$show_status()
},

show_header = function() {
self$cat_line(
clisymbols::symbol$tick, " | OK ",
colourise("F", "failure"), " ",
colourise("W", "warning"), " ",
colourise("S", "skip"), " | ",
"Context"
)
},

show_status = function(complete = FALSE) {

if (complete) {
if (self$ctxt_n_fail > 0) {
status <- crayon::red(clisymbols::symbol$cross)
} else {
status <- crayon::green(clisymbols::symbol$tick)
}
} else {
status <- spinner(self$ctxt_n)
}

col_format <- function(n, type) {
if (n == 0) {
" "
} else {
n
}
}

self$cat_tight(
"\r",
status, " | ", sprintf("%2d", self$ctxt_n_ok), " ",
col_format(self$ctxt_n_fail), " ",
col_format(self$ctxt_n_warn), " ",
col_format(self$ctxt_n_skip), " | ",
self$ctxt_name
)
},

end_context = function(context) {
time <- proc.time() - self$ctxt_start_time

self$show_status(complete = TRUE)

if (time[[3]] > 0.1) {
self$cat(crayon::white(sprintf(" [%.1f s]", time[[3]])))
}
self$cat_line()

if (self$ctxt_issues$size() > 0) {
self$rule()

issues <- self$ctxt_issues$as_list()
summary <- vapply(issues, issue_summary, FUN.VALUE = character(1))
self$cat_tight(paste(summary, collapse = "\n\n"))

self$cat_line()
self$rule()
}
},

add_result = function(context, test, result) {
self$ctxt_n <- self$ctxt_n + 1L

if (expectation_broken(result)) {
self$n_fail <- self$n_fail + 1
self$ctxt_n_fail <- self$ctxt_n_fail + 1
self$ctxt_issues$push(result)
} else if (expectation_skip(result)) {
self$n_skip <- self$n_skip + 1
self$ctxt_n_skip <- self$ctxt_n_skip + 1
self$ctxt_issues$push(result)
} else if (expectation_warning(result)) {
self$n_warn <- self$n_warn + 1
self$ctxt_n_warn <- self$ctxt_n_warn + 1
self$ctxt_issues$push(result)
} else {
self$n_ok <- self$n_ok + 1
self$ctxt_n_ok <- self$ctxt_n_ok + 1
}

self$show_status()
},

end_reporter = function() {
self$cat_line()

self$cat_line("OK: ", self$n_ok)
self$cat_line("Failed: ", self$n_fail)
self$cat_line("Warnings: ", self$n_warn)
self$cat_line("Skipped: ", self$n_skip)

if (!self$show_praise || runif(1) > 0.1)
return()

if (self$n_fail == 0) {
self$cat_line(colourise(praise(), "success"))
} else {
self$cat_line(colourise(encourage(), "error"))
}
}
),

private = list(
)
)


spinner <- function(i) {
frames <- c(
"\u280b", "\u2819", "\u2839", "\u2838", "\u283c", "\u2834",
"\u2826", "\u2827", "\u2807", "\u280f"
)
# c("⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏" )

frames[((i - 1) %% length(frames)) + 1]
}


issue_summary <- function(x) {
type <- switch(expectation_type(x),
error = "Error",
failure = "Failure",
skip = "Skip",
warning = "Warning"
)

paste0(
colourise(type, expectation_type(x)), ": ",
x$test, crayon::blue(src_loc(x$srcref)), "\n",
format(x)
)
}
7 changes: 3 additions & 4 deletions R/reporter-summary.R
Expand Up @@ -3,10 +3,9 @@ NULL

#' Test reporter: summary of errors.
#'
#' This is the most useful reporting reporter as it lets you know both which
#' tests have run successfully, as well as fully reporting information about
#' failures and errors. It is the default reporting reporter used by
#' [test_dir()] and [test_file()].
#' This is a reporter designed for interactive usage: it lets you know which
#' tests have run successfully and as well as fully reporting information about
#' failures and errors.
#'
#' You can use the `max_reports` field to control the maximum number
#' of detailed reports produced by this reporter. This is useful when running
Expand Down
2 changes: 1 addition & 1 deletion R/reporter.R
Expand Up @@ -93,5 +93,5 @@ Reporter <- R6::R6Class("Reporter",
#' @export
#' @keywords internal
default_reporter <- function() {
getOption("testthat.default_reporter", "summary")
getOption("testthat.default_reporter", "progress")
}
1 change: 1 addition & 0 deletions man/CheckReporter.Rd

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

1 change: 1 addition & 0 deletions man/DebugReporter.Rd

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

1 change: 1 addition & 0 deletions man/FailReporter.Rd

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

1 change: 1 addition & 0 deletions man/ListReporter.Rd

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

1 change: 1 addition & 0 deletions man/LocationReporter.Rd

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

1 change: 1 addition & 0 deletions man/MinimalReporter.Rd

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

1 change: 1 addition & 0 deletions man/MultiReporter.Rd

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

37 changes: 37 additions & 0 deletions man/ProgressReporter.Rd

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

1 change: 1 addition & 0 deletions man/RstudioReporter.Rd

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

1 change: 1 addition & 0 deletions man/SilentReporter.Rd

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

1 change: 1 addition & 0 deletions man/StopReporter.Rd

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

1 change: 1 addition & 0 deletions man/SummaryReporter.Rd

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

1 change: 1 addition & 0 deletions man/TapReporter.Rd

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

1 change: 1 addition & 0 deletions man/TeamcityReporter.Rd

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

0 comments on commit 317fa98

Please sign in to comment.