Skip to content

compiletest: Add an experimental new executor to replace libtest #139660

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

Zalathar
Copy link
Contributor

@Zalathar Zalathar commented Apr 11, 2025

This PR adds a new "executor" to compiletest for running the list of collected tests, to eventually replace the current dependency on unstable libtest internals.

The new executor is currently inactive by default. It must be activated explicitly by passing -n or --new-executor to compiletest, e.g. ./x test ui -- -n.

(After some amount of wider manual testing, the new executor will hopefully be made the default, and the libtest dependency can be removed. Contributors should not notice any change.)

The new executor is a stripped-down rewrite of the subset of libtest needed by compiletest.

Supported functionality

  • Specifying the number of concurrent tests with RUST_TEST_THREADS
  • Filtering and skipping tests by name (substring or exact-match)
  • Forcibly running ignored tests with --ignored
  • Optional fail-fast with --fail-fast
  • JSON output, compatible with bootstrap's parser for libtest output
  • Running each test in its own thread
  • Short backtraces that ignore the executor itself
  • Slow test detection, with a hard-coded timeout of 60 seconds
  • Capturing stdout/stderr, via #![feature(internal_output_capture)]
  • Suppressing output capture with --no-capture

Unsupported functionality

  • Non-JSON output, as this is handled by bootstrap instead
  • Separate code path for concurrency=1, as the concurrent path should handle this case naturally
  • Fallback to running tests synchronously if new threads can't be spawned
  • Special handling of hosts that don't support basic functionality like threads or timers
    • Our ability to test targets should be unaffected
  • Graceful handling of some edge cases that could occur in arbitrary user-written unit tests, but would represent bugs in compiletest
  • Due to the current need for output capture, the new executor is still not entirely written in stable Rust

r? jieyouxu

@Zalathar Zalathar added the A-compiletest Area: The compiletest test runner label Apr 11, 2025
@rustbot rustbot added A-testsuite Area: The testsuite used to check the correctness of rustc S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-bootstrap Relevant to the bootstrap subteam: Rust's build system (x.py and src/bootstrap) labels Apr 11, 2025
@rustbot
Copy link
Collaborator

rustbot commented Apr 11, 2025

Some changes occurred in src/tools/compiletest

cc @jieyouxu

@rust-log-analyzer

This comment has been minimized.

@Zalathar Zalathar force-pushed the new-executor branch 2 times, most recently from 62d2626 to 3f2fa86 Compare April 11, 2025 08:28
@Zalathar Zalathar changed the title compiletest: Add an experimental "new" executor to replace libtest compiletest: Add an experimental new executor to replace libtest Apr 11, 2025
@jieyouxu
Copy link
Member

pro gamer move

I'll look at this tmrw/Sunday.

@petrochenkov
Copy link
Contributor

petrochenkov commented Apr 11, 2025

and the libtest dependency can be removed

Is there some history behind this change?
Why is it so important to remove the libtest dependency?

@jieyouxu
Copy link
Member

jieyouxu commented Apr 11, 2025

and the libtest dependency can be removed

Is there some history behind this change? Why is it so important to remove the libtest dependency?

This is in the context of the stage 0 std redesign (#119899). Currently, compiletest depends on in-tree libtest (unless compiletest-use-stage0-libtest = true is specified). For compiler workflows, this means that compiletest will be rebuilt when iterating on compiler changes which feels quite bad with the stage 0 std redesign otherwise (see #t-infra/bootstrap > Review thread: stage 0 redesign PR @ 💬 discussions).

If we no longer depend on libtest, then we won't need this compiletest-use-stage0-libtest = true workaround/hack.

@Zalathar
Copy link
Contributor Author

Zalathar commented Apr 11, 2025

Depending on libtest internals is a big maintenance headache for compiletest. We already have a bunch of annoying hacks in compiletest to work around limitations that libtest imposes. And changing libtest to better meet compiletest's needs is very risky, because libtest is the backbone of testing across most of the wider Rust ecosystem.

What has made this change even more desirable is the upcoming stage 0 redesign (#119899). Currently compiletest relies on an in-tree libtest built with the stage 0 compiler. But after the redesign, stage 0 builds of the standard library will no longer be supported, so compiletest will have to rely on either a stage 1 build of libtest (resulting in unacceptable workflow regressions for compiler contributors), or the pre-built stage 0 libtest (which can go out of sync with the in-tree version, causing various bootstrapping problems).

This replacement still needs unstable library features (for now), but internal_output_capture is a much smaller unstable surface area. And in the future it should be possible to work towards removing the need for output capture in compiletest, making it 100% compatible with stable Rust. That removes a lot of the maintenance concerns associated with wanting to build it with the stage 0 compiler.

@Zalathar
Copy link
Contributor Author

Tweaked some comments (diff); no functional change.

Copy link
Member

@jieyouxu jieyouxu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for working on this! The new executor implementation here is very nice to follow, and having more control over how compiletest actually executes the tests is very much appreciated!

I left two nits, but otherwise the changes look good to me.


/// Applies command-line arguments for filtering/skipping tests by name.
///
/// Adapted from `filter_tests` in libtest.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: can you leave me a note here to "reconsider test filtering behavior in new executor"? One gripe I had with libtest's test filtering was that after compiletest passes to libtest the canonicalized test name (based on a relative path), the default filter logic is a naive substring match which can often produce some surprising running more tests than expected behavior. A benefit of rolling our own executor here is to allow us to redesign the filter logic (as a possibility).

///
/// Copied from `get_concurrency` in libtest.
fn get_concurrency() -> usize {
if let Ok(value) = env::var("RUST_TEST_THREADS") {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Discussion: Since the only intended consumer of compiletest's cli is bootstrap nowadays(rustdoc-gui-test uses compiletest programmatically), should this instead be a cli flag instead of an env var? Although I guess even if it was an cli flag, we need also need to process RUST_TEST_THREADS anyway.

@jieyouxu
Copy link
Member

@rustbot author

@rustbot rustbot added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Apr 14, 2025
@rustbot
Copy link
Collaborator

rustbot commented Apr 14, 2025

Reminder, once the PR becomes ready for a review, use @rustbot ready.

@bors
Copy link
Collaborator

bors commented Apr 14, 2025

☔ The latest upstream changes (presumably #139781) made this pull request unmergeable. Please resolve the merge conflicts.

The new executor can be enabled by passing `--new-executor` or `-n` to
compiletest.

For example: `./x test ui -- -n`
@Zalathar
Copy link
Contributor Author

Rebased to fix trivial conflicts, then added some FIXMEs to filter_tests and get_concurrency to address feedback (diff).

@rustbot ready

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Apr 15, 2025
Copy link
Member

@jieyouxu jieyouxu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks

@jieyouxu
Copy link
Member

@bors r+ rollup

@bors
Copy link
Collaborator

bors commented Apr 15, 2025

📌 Commit e3d6813 has been approved by jieyouxu

It is now in the queue for this repository.

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Apr 15, 2025
Zalathar added a commit to Zalathar/rust that referenced this pull request Apr 15, 2025
compiletest: Add an experimental new executor to replace libtest

This PR adds a new "executor" to compiletest for running the list of collected tests, to eventually replace the current dependency on unstable libtest internals.

The new executor is currently inactive by default. It must be activated explicitly by passing `-n` or `--new-executor` to compiletest, e.g. `./x test ui -- -n`.

(After some amount of wider manual testing, the new executor will hopefully be made the default, and the libtest dependency can be removed. Contributors should not notice any change.)

The new executor is a stripped-down rewrite of the subset of libtest needed by compiletest.

# Supported functionality
- Specifying the number of concurrent tests with `RUST_TEST_THREADS`
- Filtering and skipping tests by name (substring or exact-match)
- Forcibly running ignored tests with `--ignored`
- Optional fail-fast with `--fail-fast`
- JSON output, compatible with bootstrap's parser for libtest output
- Running each test in its own thread
- Short backtraces that ignore the executor itself
- Slow test detection, with a hard-coded timeout of 60 seconds
- Capturing stdout/stderr, via `#![feature(internal_output_capture)]`
- Suppressing output capture with `--no-capture`

# Unsupported functionality
- Non-JSON output, as this is handled by bootstrap instead
- Separate code path for concurrency=1, as the concurrent path should handle this case naturally
- Fallback to running tests synchronously if new threads can't be spawned
- Special handling of hosts that don't support basic functionality like threads or timers
  - Our ability to test *targets* should be unaffected
- Graceful handling of some edge cases that could occur in arbitrary user-written unit tests, but would represent bugs in compiletest
- Due to the current need for output capture, the new executor is still not entirely written in stable Rust

---
r? jieyouxu
bors added a commit to rust-lang-ci/rust that referenced this pull request Apr 15, 2025
Rollup of 17 pull requests

Successful merges:

 - rust-lang#138374 (Enable contracts for const functions)
 - rust-lang#138380 (ci: add runners for vanilla LLVM 20)
 - rust-lang#138393 (Allow const patterns of matches to contain pattern types)
 - rust-lang#139517 (std: sys: process: uefi: Use NULL stdin by default)
 - rust-lang#139554 (std: add Output::exit_ok)
 - rust-lang#139660 (compiletest: Add an experimental new executor to replace libtest)
 - rust-lang#139669 (Overhaul `AssocItem`)
 - rust-lang#139671 (Proc macro span API redesign: Replace proc_macro::SourceFile by Span::{file, local_file})
 - rust-lang#139750 (std/thread: Use default stack size from menuconfig for NuttX)
 - rust-lang#139772 (Remove `hir::Map`)
 - rust-lang#139785 (Let CStrings be either 1 or 2 byte aligned.)
 - rust-lang#139789 (do not unnecessarily leak auto traits in item bounds)
 - rust-lang#139791 (drop global where-bounds before merging candidates)
 - rust-lang#139798 (normalize: prefer `ParamEnv` over `AliasBound` candidates)
 - rust-lang#139822 (Fix: Map EOPNOTSUPP to ErrorKind::Unsupported on Unix)
 - rust-lang#139833 (Fix some HIR pretty-printing problems)
 - rust-lang#139836 (Basic tests of MPMC receiver cloning)

r? `@ghost`
`@rustbot` modify labels: rollup
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-compiletest Area: The compiletest test runner A-testsuite Area: The testsuite used to check the correctness of rustc S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. T-bootstrap Relevant to the bootstrap subteam: Rust's build system (x.py and src/bootstrap)
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants