Skip to content

Commit aa6ddf2

Browse files
committed
Merge commit '89b63dd137f649e50ce04c0141b154d80e7b642b'
2 parents 889de40 + 89b63dd commit aa6ddf2

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+573
-447
lines changed

.Rbuildignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,5 @@
2323
^[\.]?air\.toml$
2424
^\.vscode$
2525
^\.git-blame-ignore-rev$
26+
^CLAUDE\.md$
27+
^\.claude$

.claude/settings.local.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"permissions": {
3+
"allow": [
4+
"Bash(find:*)"
5+
],
6+
"deny": []
7+
}
8+
}

CLAUDE.md

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## About This Project
6+
7+
testthat is R's most popular unit testing framework, used by thousands of CRAN packages. It provides functions to make testing R code as fun and addictive as possible, with clear expectations, visual progress indicators, and seamless integration with R package development workflows.
8+
9+
## Key Development Commands
10+
11+
### Testing
12+
- `devtools::test()` or `Ctrl/Cmd+Shift+T` in RStudio - Run all tests
13+
- `devtools::test_file("tests/testthat/test-filename.R")` - Run tests in a specific file
14+
- `testthat::test_local()` - Run tests for local source package
15+
- `testthat::test_package("testthat")` - Run tests for installed package
16+
- `R CMD check` - Full package check including tests
17+
18+
### Building and Installation
19+
- `devtools::load_all()` or `Ctrl/Cmd+Shift+L` - Load package for development
20+
- `devtools::document()` - Generate documentation
21+
- `devtools::check()` - Run R CMD check
22+
- `devtools::install()` - Install package locally
23+
24+
## Core Architecture
25+
26+
### Main Components
27+
28+
1. **Core Testing Functions** (`R/test-that.R`, `R/test-package.R`):
29+
- `test_that()` - The fundamental testing function
30+
- `test_local()`, `test_package()`, `test_check()` - Different ways to run test suites
31+
32+
2. **Expectations** (`R/expect-*.R`):
33+
- Modular expectation functions (equality, conditions, types, etc.)
34+
- Each expectation type has its own file following the pattern `expect-[type].R`
35+
36+
3. **Reporters** (`R/reporter*.R`):
37+
- Different output formats for test results
38+
- Object-oriented design with base `Reporter` class
39+
- Includes check, debug, progress, summary, JUnit, TAP formats
40+
41+
4. **Snapshot Testing** (`R/snapshot*.R`):
42+
- Value snapshots, file snapshots, output snapshots
43+
- Automatic management and comparison of expected outputs
44+
45+
5. **Parallel Testing** (`R/parallel*.R`):
46+
- Multi-core test execution
47+
- Configuration via `Config/testthat/parallel: true` in DESCRIPTION
48+
49+
6. **Mocking** (`R/mock*.R`, `R/mock2*.R`):
50+
- Function mocking capabilities
51+
- Both legacy (`mock.R`) and modern (`mock2*.R`) implementations
52+
53+
### Key Design Patterns
54+
55+
- **Editions**: testthat has different "editions" with varying behavior, controlled by `Config/testthat/edition`
56+
- **Reporters**: Extensible reporting system using R6 classes
57+
- **Lazy Evaluation**: Expectations use substitute() and lazy evaluation for better error messages
58+
- **C++ Integration**: Core functionality implemented in C++ for performance
59+
60+
### File Organization
61+
62+
- `R/` - All R source code, organized by functionality
63+
- `src/` - C++ source code and Makevars
64+
- `inst/include/testthat/` - C++ headers for other packages to use
65+
- `tests/testthat/` - Package's own comprehensive test suite
66+
- `vignettes/` - Documentation on testing concepts and workflows
67+
68+
### Important Configuration
69+
70+
The package uses several DESCRIPTION fields for configuration:
71+
- `Config/testthat/edition: 3` - Sets testthat edition
72+
- `Config/testthat/parallel: true` - Enables parallel testing
73+
- `Config/testthat/start-first` - Tests to run first in parallel mode
74+
75+
### C++ Testing Infrastructure
76+
77+
testthat provides C++ testing capabilities via Catch framework:
78+
- Headers in `inst/include/testthat/`
79+
- Test runner infrastructure in `src/test-runner.cpp`
80+
- Integration with R's testing system
81+
82+
### Snapshot Testing Workflow
83+
84+
- Snapshots stored in `tests/testthat/_snaps/`
85+
- Different snapshot types: values, files, output
86+
- Version-specific snapshots for different R versions
87+
- Use `testthat::snapshot_accept()` to update snapshots
88+
89+
This codebase prioritizes backward compatibility, comprehensive testing, and clear, descriptive error messages to help R developers write better tests.

R/expect-comparison.R

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,12 @@
1919
#' @name comparison-expectations
2020
NULL
2121

22-
expect_compare <- function(operator = c("<", "<=", ">", ">="), act, exp) {
22+
expect_compare_ <- function(
23+
operator = c("<", "<=", ">", ">="),
24+
act,
25+
exp,
26+
trace_env = caller_env()
27+
) {
2328
operator <- match.arg(operator)
2429
op <- match.fun(operator)
2530

@@ -34,26 +39,25 @@ expect_compare <- function(operator = c("<", "<=", ">", ">="), act, exp) {
3439
if (length(cmp) != 1 || !is.logical(cmp)) {
3540
abort("Result of comparison must be a single logical value")
3641
}
37-
expect(
38-
if (!is.na(cmp)) cmp else FALSE,
39-
sprintf(
42+
if (!isTRUE(cmp)) {
43+
msg <- sprintf(
4044
"%s is %s %s. Difference: %.3g",
4145
act$lab,
4246
msg,
4347
exp$lab,
4448
act$val - exp$val
45-
),
46-
trace_env = caller_env()
47-
)
48-
invisible(act$val)
49+
)
50+
return(fail(msg, trace_env = trace_env))
51+
}
52+
pass(act$val)
4953
}
5054
#' @export
5155
#' @rdname comparison-expectations
5256
expect_lt <- function(object, expected, label = NULL, expected.label = NULL) {
5357
act <- quasi_label(enquo(object), label, arg = "object")
5458
exp <- quasi_label(enquo(expected), expected.label, arg = "expected")
5559

56-
expect_compare("<", act, exp)
60+
expect_compare_("<", act, exp)
5761
}
5862

5963
#' @export
@@ -62,7 +66,7 @@ expect_lte <- function(object, expected, label = NULL, expected.label = NULL) {
6266
act <- quasi_label(enquo(object), label, arg = "object")
6367
exp <- quasi_label(enquo(expected), expected.label, arg = "expected")
6468

65-
expect_compare("<=", act, exp)
69+
expect_compare_("<=", act, exp)
6670
}
6771

6872
#' @export
@@ -71,7 +75,7 @@ expect_gt <- function(object, expected, label = NULL, expected.label = NULL) {
7175
act <- quasi_label(enquo(object), label, arg = "object")
7276
exp <- quasi_label(enquo(expected), expected.label, arg = "expected")
7377

74-
expect_compare(">", act, exp)
78+
expect_compare_(">", act, exp)
7579
}
7680

7781
#' @export
@@ -80,7 +84,7 @@ expect_gte <- function(object, expected, label = NULL, expected.label = NULL) {
8084
act <- quasi_label(enquo(object), label, arg = "object")
8185
exp <- quasi_label(enquo(expected), expected.label, arg = "expected")
8286

83-
expect_compare(">=", act, exp)
87+
expect_compare_(">=", act, exp)
8488
}
8589

8690

R/expect-condition.R

Lines changed: 30 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ expect_error <- function(
115115
label = NULL
116116
) {
117117
if (edition_get() >= 3) {
118-
expect_condition_matching(
118+
expect_condition_matching_(
119119
"error",
120120
{{ object }},
121121
regexp = regexp,
@@ -138,8 +138,10 @@ expect_error <- function(
138138

139139
# Access error fields with `[[` rather than `$` because the
140140
# `$.Throwable` from the rJava package throws with unknown fields
141-
expect(is.null(msg), msg, info = info, trace = act$cap[["trace"]])
142-
invisible(act$val %||% act$cap)
141+
if (!is.null(msg)) {
142+
return(fail(msg, info = info, trace = act$cap[["trace"]]))
143+
}
144+
pass(act$val %||% act$cap)
143145
}
144146
}
145147

@@ -161,7 +163,7 @@ expect_warning <- function(
161163
warn("The `all` argument is deprecated")
162164
}
163165

164-
expect_condition_matching(
166+
expect_condition_matching_(
165167
"warning",
166168
{{ object }},
167169
regexp = regexp,
@@ -186,9 +188,10 @@ expect_warning <- function(
186188
...,
187189
cond_type = "warnings"
188190
)
189-
expect(is.null(msg), msg, info = info)
190-
191-
invisible(act$val)
191+
if (!is.null(msg)) {
192+
return(fail(msg, info = info))
193+
}
194+
pass(act$val)
192195
}
193196
}
194197

@@ -205,7 +208,7 @@ expect_message <- function(
205208
label = NULL
206209
) {
207210
if (edition_get() >= 3) {
208-
expect_condition_matching(
211+
expect_condition_matching_(
209212
"message",
210213
{{ object }},
211214
regexp = regexp,
@@ -218,9 +221,10 @@ expect_message <- function(
218221
} else {
219222
act <- quasi_capture(enquo(object), label, capture_messages)
220223
msg <- compare_messages(act$cap, act$lab, regexp = regexp, all = all, ...)
221-
expect(is.null(msg), msg, info = info)
222-
223-
invisible(act$val)
224+
if (!is.null(msg)) {
225+
return(fail(msg, info = info))
226+
}
227+
pass(act$val)
224228
}
225229
}
226230

@@ -236,7 +240,7 @@ expect_condition <- function(
236240
label = NULL
237241
) {
238242
if (edition_get() >= 3) {
239-
expect_condition_matching(
243+
expect_condition_matching_(
240244
"condition",
241245
{{ object }},
242246
regexp = regexp,
@@ -262,13 +266,14 @@ expect_condition <- function(
262266
inherit = inherit,
263267
cond_type = "condition"
264268
)
265-
expect(is.null(msg), msg, info = info, trace = act$cap[["trace"]])
266-
267-
invisible(act$val %||% act$cap)
269+
if (!is.null(msg)) {
270+
return(fail(msg, info = info, trace = act$cap[["trace"]]))
271+
}
272+
pass(act$val %||% act$cap)
268273
}
269274
}
270275

271-
expect_condition_matching <- function(
276+
expect_condition_matching_ <- function(
272277
base_class,
273278
object,
274279
regexp = NULL,
@@ -303,17 +308,17 @@ expect_condition_matching <- function(
303308

304309
# Access error fields with `[[` rather than `$` because the
305310
# `$.Throwable` from the rJava package throws with unknown fields
306-
expect(
307-
is.null(msg),
308-
msg,
309-
info = info,
310-
trace = act$cap[["trace"]],
311-
trace_env = trace_env
312-
)
313-
311+
if (!is.null(msg)) {
312+
return(fail(
313+
msg,
314+
info = info,
315+
trace = act$cap[["trace"]],
316+
trace_env = trace_env
317+
))
318+
}
314319
# If a condition was expected, return it. Otherwise return the value
315320
# of the expression.
316-
invisible(if (expected) act$cap else act$val)
321+
pass(if (expected) act$cap else act$val)
317322
}
318323

319324
# -------------------------------------------------------------------------

R/expect-constant.R

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,14 @@ NULL
3030
#' @rdname logical-expectations
3131
expect_true <- function(object, info = NULL, label = NULL) {
3232
act <- quasi_label(enquo(object), label, arg = "object")
33-
expect_waldo_constant(act, TRUE, info = info, ignore_attr = TRUE)
33+
expect_waldo_constant_(act, TRUE, info = info, ignore_attr = TRUE)
3434
}
3535

3636
#' @export
3737
#' @rdname logical-expectations
3838
expect_false <- function(object, info = NULL, label = NULL) {
3939
act <- quasi_label(enquo(object), label, arg = "object")
40-
expect_waldo_constant(act, FALSE, info = info, ignore_attr = TRUE)
40+
expect_waldo_constant_(act, FALSE, info = info, ignore_attr = TRUE)
4141
}
4242

4343
#' Does code return `NULL`?
@@ -56,13 +56,18 @@ expect_false <- function(object, info = NULL, label = NULL) {
5656
#' show_failure(expect_null(y))
5757
expect_null <- function(object, info = NULL, label = NULL) {
5858
act <- quasi_label(enquo(object), label, arg = "object")
59-
60-
expect_waldo_constant(act, NULL, info = info)
59+
expect_waldo_constant_(act, NULL, info = info)
6160
}
6261

6362
# helpers -----------------------------------------------------------------
6463

65-
expect_waldo_constant <- function(act, constant, info, ...) {
64+
expect_waldo_constant_ <- function(
65+
act,
66+
constant,
67+
info,
68+
...,
69+
trace_env = caller_env()
70+
) {
6671
comp <- waldo_compare(
6772
act$val,
6873
constant,
@@ -71,17 +76,15 @@ expect_waldo_constant <- function(act, constant, info, ...) {
7176
...
7277
)
7378

74-
expect(
75-
length(comp) == 0,
76-
sprintf(
79+
if (length(comp) != 0) {
80+
msg <- sprintf(
7781
"%s is not %s\n\n%s",
7882
act$lab,
7983
deparse(constant),
8084
paste0(comp, collapse = "\n\n")
81-
),
82-
info = info,
83-
trace_env = caller_env()
84-
)
85+
)
86+
return(fail(msg, info = info, trace_env = trace_env))
87+
}
8588

86-
invisible(act$val)
89+
pass(act$val)
8790
}

0 commit comments

Comments
 (0)