mirror of
https://github.com/zed-industries/zed.git
synced 2026-05-31 19:05:00 +07:00
Introduce a skill for #[gpui::test] (#57806)
Release Notes: - N/A or Added/Fixed/Improved ...
This commit is contained in:
parent
9a7a5d685d
commit
9b4737b0d7
1 changed files with 160 additions and 0 deletions
160
.agents/skills/gpui-test/SKILL.md
Normal file
160
.agents/skills/gpui-test/SKILL.md
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
---
|
||||
name: gpui-test
|
||||
description: >-
|
||||
Use when writing, debugging, or reproducing GPUI tests in Zed, including
|
||||
gpui::test arguments, TestAppContext parameters, scheduler seeds,
|
||||
ITERATIONS/SEED reproduction, parking failures, and pending task traces.
|
||||
---
|
||||
|
||||
# GPUI Test Debugging
|
||||
|
||||
Use this skill when the user asks about `#[gpui::test]`, GPUI test seeds or iterations, deterministic scheduler failures, parking/pending task failures, or how to reproduce a flaky GPUI test.
|
||||
|
||||
## What `#[gpui::test]` does
|
||||
|
||||
`#[gpui::test]` expands to a normal Rust `#[test]`, so it runs under standard Rust test runners such as `cargo test` and `cargo nextest`.
|
||||
|
||||
It wraps the body in GPUI's deterministic test dispatcher/scheduler and can run the same test multiple times with different seeds. The seed controls scheduler task interleavings and any `StdRng` argument injected into the test.
|
||||
|
||||
The macro supports both synchronous and asynchronous tests.
|
||||
|
||||
### Supported function arguments
|
||||
|
||||
The macro recognizes arguments by type name:
|
||||
|
||||
| Test kind | Supported arguments |
|
||||
| --- | --- |
|
||||
| Sync and async | `&TestAppContext`, `&mut TestAppContext`, `StdRng` |
|
||||
| Async only | `BackgroundExecutor` |
|
||||
| Sync only | `&App`, `&mut App` |
|
||||
|
||||
`StdRng` is seeded from the current GPUI test seed, and `BackgroundExecutor` is backed by the same deterministic test dispatcher.
|
||||
|
||||
### Attribute arguments
|
||||
|
||||
Use these forms on `#[gpui::test(arguments)]`:
|
||||
|
||||
- No arguments: runs once with seed `0`, unless `SEED` is set.
|
||||
- `seed = N`: adds a single explicit seed.
|
||||
- `seeds(...)`: adds multiple explicit seeds.
|
||||
- `iterations = N`: runs sequential seeds starting at `0` by default.
|
||||
- `retries = N`: retries a failing run up to `N` times before surfacing the failure.
|
||||
- `on_failure = "path::to::function"`: calls the function after final failure, before resuming the panic.
|
||||
- `iterations` can be combined with explicit `seed` / `seeds`; explicit seeds are appended to the `0..iterations` range.
|
||||
- If the `SEED` environment variable is set, it takes precedence over explicit seeds.
|
||||
- With `SEED=N` and `ITERATIONS=M` or `iterations = M`, the harness runs seeds `N..N+M`.
|
||||
|
||||
## Environment variables
|
||||
|
||||
### GPUI test macro / scheduler execution
|
||||
|
||||
- `SEED=<u64>` — chooses the scheduler seed. Use this to reproduce a failure printed as `failing seed: N`. It also seeds injected `StdRng` arguments. For `#[gpui::property_test]`, it controls the scheduler seed and GPUI applies it to the proptest config for deterministic case generation.
|
||||
- `ITERATIONS=<usize>` — overrides the `iterations = ...` value at runtime. Use to sweep many seeds without editing the test.
|
||||
- `PENDING_TRACES=1` or `PENDING_TRACES=true` — captures and prints pending task traces when the test scheduler panics with `Parking forbidden`. Use this when `run_until_parked()` or teardown reports pending work.
|
||||
- `GPUI_RUN_UNTIL_PARKED_LOG=1` — logs when `allow_parking()` is enabled. Use to find tests that explicitly permit parking/pending work.
|
||||
- `DEBUG_SCHEDULER=1` — prints scheduler clock/timer debugging from `scheduler::TestScheduler`.
|
||||
|
||||
### Lower-level scheduler tests
|
||||
|
||||
- `SCHEDULER_NONINTERACTIVE=1` — suppresses interactive seed progress output in `scheduler::TestScheduler::many`. This does not affect the `#[gpui::test]` harness path.
|
||||
|
||||
### General Rust test debugging vars often useful with GPUI tests
|
||||
|
||||
- `RUST_BACKTRACE=1` or `RUST_BACKTRACE=full` — show panic backtraces.
|
||||
- `RUST_LOG=<filter>` — enable logs when the test initializes logging.
|
||||
- `ZED_HEADLESS=1` — forces GPUI platform guessing toward headless mode; useful for tests that otherwise interact with platform/window setup.
|
||||
|
||||
Prefer env vars over editing the test when narrowing a reproduction.
|
||||
|
||||
## Reproducing a specific GPUI test
|
||||
|
||||
1. Identify the crate/package and test name.
|
||||
|
||||
2. Run the narrowest test filter first, skip to 3. if a failing seed is known.
|
||||
|
||||
```sh
|
||||
cargo -q test -p <crate-name> <test_name> -- --nocapture
|
||||
```
|
||||
|
||||
3. If the failure mentions a seed, rerun exactly that seed.
|
||||
|
||||
```sh
|
||||
SEED=<seed> cargo -q test -p <crate-name> <test_name> -- --nocapture
|
||||
```
|
||||
|
||||
4. If the failure is flaky and no seed is known, sweep seeds.
|
||||
|
||||
```sh
|
||||
ITERATIONS=100 cargo -q test -p <crate-name> <test_name> -- --nocapture
|
||||
```
|
||||
|
||||
When the harness prints `failing seed: <seed>`, switch to `SEED=<seed>` for all future debugging.
|
||||
|
||||
5. If the failure is `Parking forbidden`, rerun with pending traces.
|
||||
|
||||
```sh
|
||||
PENDING_TRACES=1 cargo -q test -p <crate-name> <test_name> -- --nocapture
|
||||
```
|
||||
|
||||
If a failing seed was printed or is already known, include it too:
|
||||
|
||||
```sh
|
||||
SEED=<seed> PENDING_TRACES=1 cargo -q test -p <crate-name> <test_name> -- --nocapture
|
||||
```
|
||||
|
||||
Inspect the pending traces for a task that was spawned but not awaited, detached, completed, or intentionally allowed to park.
|
||||
|
||||
6. If timing or timer advancement is involved, prefer GPUI scheduler timers in tests:
|
||||
|
||||
```rust
|
||||
cx.background_executor().timer(duration).await;
|
||||
```
|
||||
|
||||
Avoid `smol::Timer::after(...)` in GPUI tests that rely on `run_until_parked()`, because GPUI's scheduler may not track it.
|
||||
|
||||
7. Minimize the reproduction.
|
||||
- Keep the failing `SEED` fixed.
|
||||
- Reduce `ITERATIONS` to `1` or remove it once a seed is known.
|
||||
- Remove unrelated setup only after confirming the same seed still fails.
|
||||
- Preserve scheduler-sensitive awaits/yields; removing them can mask the bug.
|
||||
- If randomness is test-controlled via `StdRng`, log or assert the generated scenario after fixing the scheduler seed.
|
||||
|
||||
8. Validate the fix.
|
||||
- Run the fixed seed.
|
||||
- Run a modest seed sweep, e.g. `ITERATIONS=20`, if the failure was scheduler-sensitive.
|
||||
- Run the relevant crate's test filter or broader suite if the touched code has shared behavior.
|
||||
|
||||
## Common diagnosis patterns
|
||||
|
||||
### Seed-dependent assertion failure
|
||||
|
||||
Likely caused by a scheduler interleaving or by `StdRng`-driven test data. Fix `SEED`, reproduce, and inspect which task or generated scenario differs.
|
||||
|
||||
### `Parking forbidden`
|
||||
|
||||
Usually means a foreground/background task is still pending when the scheduler expected the test to make progress or finish. Look for:
|
||||
|
||||
- A task that should be awaited but was dropped.
|
||||
- A task that should be detached with error logging.
|
||||
- A timer or receiver that is waiting forever.
|
||||
- A missing `cx.run_until_parked()` after triggering async work in a test.
|
||||
- A missing `cx.advance_clock(...)` to wait for debounced work in a test.
|
||||
- Use of non-GPUI timers or executors that the test scheduler cannot drive.
|
||||
|
||||
Rerun with `PENDING_TRACES=1` before changing code.
|
||||
|
||||
### Non-determinism / wrong thread
|
||||
|
||||
The scheduler can report activity from an unexpected thread. Look for work escaping GPUI's foreground/background executors, direct thread spawns, or external async runtimes not controlled by the test dispatcher.
|
||||
|
||||
### Tests pass alone but fail in sweeps
|
||||
|
||||
Use the failing seed from sweep output. Avoid assuming test order unless the runner is explicitly serial. Check globals, leaked entities/tasks, and state not reset by test initialization.
|
||||
|
||||
## Writing GPUI tests
|
||||
|
||||
- Prefer `#[gpui::test]` for tests that need `TestAppContext`, deterministic executors, fake time, or scheduler interleaving coverage.
|
||||
- Add `iterations = N` when the test is intentionally checking interleavings.
|
||||
- Use `StdRng` as a test argument when randomized test data should follow the same seed as the scheduler.
|
||||
- Use `cx.background_executor().timer(duration).await` for delays/timeouts in GPUI tests.
|
||||
- Do not add or increase `retries` while fixing a test unless the user explicitly asks or the test already documents why probabilistic tolerance is intentional. Retries can mask the failure instead of fixing it.
|
||||
Loading…
Reference in a new issue