Add init_expr keyword argument to runtests#237
Conversation
Add a new `init_expr` keyword argument that behaves like `worker_init_expr` but also runs when `nworkers=0`. When `nworkers>0`, it runs on each worker process before any tests. When `nworkers=0`, it runs in the main process before test execution. It is an error to specify both `init_expr` and `worker_init_expr`. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR adds a new init_expr keyword argument to runtests that provides a unified way to run initialization code before tests, working consistently across both single-process (nworkers=0) and multi-process (nworkers>0) execution modes. This replaces the need for worker_init_expr which only works in multi-process mode.
Changes:
- Added
init_exprparameter toruntestsfunction with validation - Implemented execution logic for both single-process and multi-process modes
- Added mutual exclusivity check to prevent using both
init_exprandworker_init_expr
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| test/testfiles/_init_expr_test.jl | New test file that verifies init_expr sets a global variable before test execution |
| test/integrationtests.jl | Integration tests covering init_expr functionality in both execution modes and error handling |
| src/ReTestItems.jl | Core implementation adding init_expr parameter, validation, execution logic, and documentation |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
src/ReTestItems.jl
Outdated
| timeout_profile_wait >= 0 || throw(ArgumentError("`timeout_profile_wait` must be a non-negative number, got $(repr(timeout_profile_wait))")) | ||
| test_end_expr.head === :block || throw(ArgumentError("`test_end_expr` must be a `:block` expression, got a `$(repr(test_end_expr.head))` expression")) | ||
| init_expr.head === :block || throw(ArgumentError("`init_expr` must be a `:block` expression, got a `$(repr(init_expr.head))` expression")) | ||
| (length(init_expr.args) > 0 && length(worker_init_expr.args) > 0) && throw(ArgumentError("Cannot specify both `init_expr` and `worker_init_expr`. Use `init_expr` instead.")) |
There was a problem hiding this comment.
The validation allows both init_expr and worker_init_expr to be specified as long as one is empty (default Expr(:block)). This could lead to confusion where a user explicitly passes init_expr=Expr(:block) alongside worker_init_expr with actual code, and it would be accepted. Consider checking if either parameter was explicitly provided rather than just checking if they contain code. Alternatively, document this behavior clearly or reject any combination where both parameters are non-default.
src/ReTestItems.jl
Outdated
| # the workers won't be able to collect it if they get under memory pressure. | ||
| GC.gc(true) | ||
| # Use init_expr if set, otherwise use worker_init_expr (they are mutually exclusive) | ||
| effective_init_expr = length(cfg.init_expr.args) > 0 ? cfg.init_expr : cfg.worker_init_expr |
There was a problem hiding this comment.
This logic to select between init_expr and worker_init_expr is duplicated in three places (lines 485, 656, and implicitly at 497, 663, 762). Consider extracting this into a helper function or computing effective_init_expr once in the _Config struct to reduce duplication and ensure consistency.
| effective_init_expr = length(cfg.init_expr.args) > 0 ? cfg.init_expr : cfg.worker_init_expr | |
| _effective_init_expr(cfg) = length(cfg.init_expr.args) > 0 ? cfg.init_expr : cfg.worker_init_expr | |
| effective_init_expr = _effective_init_expr(cfg) |
Summary
init_exprkeyword argument that runs initialization code before any testsworker_init_expr,init_expralso works whennworkers=0nworkers>0, runs on each worker process (same asworker_init_expr)nworkers=0, runs in the main process before test executioninit_exprandworker_init_exprare specifiedTest plan
init_exprwithnworkers=0init_exprwithnworkers>0init_exprandworker_init_exprthrowsArgumentError🤖 Generated with Claude Code