Skip to content

Commit e62ad61

Browse files
branchseerclaude
andcommitted
feat(e2e): detect [send-me-ctrl-c] and send SIGINT to process
Implement automatic Ctrl+C detection in e2e tests to enable testing of interrupted task behavior and cache invalidation. Changes: - Add send_ctrlc dependency with tokio feature for cross-platform SIGINT support - Update test runner to spawn processes with spawn_interruptible() - Detect [send-me-ctrl-c] magic string in stdout and send SIGINT - Add new e2e test case verifying "cache not updated: execution interrupted" Test Details: - ctrl-c-interruption fixture with Node.js script that handles SIGINT gracefully - Script prints magic string, then waits indefinitely for signal - Test verifies both runs show cache miss (proving cache wasn't saved) - Process exits with code 0 after receiving SIGINT Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
1 parent 2356bc0 commit e62ad61

7 files changed

Lines changed: 98 additions & 2 deletions

File tree

Cargo.lock

Lines changed: 12 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/vite_task_bin/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ copy_dir = { workspace = true }
2626
cow-utils = { workspace = true }
2727
insta = { workspace = true, features = ["glob", "json", "redactions", "filters", "ron"] }
2828
regex = { workspace = true }
29+
send_ctrlc = { version = "0.6.0", features = ["tokio"] }
2930
serde = { workspace = true, features = ["derive", "rc"] }
3031
tempfile = { workspace = true }
3132
toml = { workspace = true }
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// Handle SIGINT gracefully
2+
process.on('SIGINT', () => {
3+
console.log('Received SIGINT, exiting gracefully');
4+
process.exit(0);
5+
});
6+
7+
// Print magic string to trigger Ctrl+C
8+
console.log('[send-me-ctrl-c]');
9+
10+
// Keep the process alive to receive SIGINT
11+
// (process will be interrupted by the test infrastructure)
12+
setInterval(() => {}, 1000);
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"name": "ctrl-c-interruption",
3+
"scripts": {
4+
"interrupt-test": "node interrupt-test.js"
5+
}
6+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
[[e2e]]
2+
name = "interrupted task does not update cache"
3+
steps = [
4+
"vite run interrupt-test",
5+
"vite run interrupt-test",
6+
]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
---
2+
source: crates/vite_task_bin/tests/e2e_snapshots/main.rs
3+
assertion_line: 309
4+
expression: e2e_outputs
5+
input_file: crates/vite_task_bin/tests/e2e_snapshots/fixtures/ctrl-c-interruption
6+
---
7+
> vite run interrupt-test
8+
$ node interrupt-test.js
9+
[send-me-ctrl-c]
10+
Received SIGINT, exiting gracefully
11+
12+
13+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
14+
Vite+ Task RunnerExecution Summary
15+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
16+
17+
Statistics: 1 tasks0 cache hits1 cache misses
18+
Performance: 0% cache hit rate
19+
20+
Task Details:
21+
────────────────────────────────────────────────
22+
[1] ctrl-c-interruption#interrupt-test: $ node interrupt-test.js
23+
Cache miss: no previous cache entry found
24+
Cache not updated: execution interrupted
25+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
26+
27+
> vite run interrupt-test
28+
$ node interrupt-test.js
29+
[send-me-ctrl-c]
30+
Received SIGINT, exiting gracefully
31+
32+
33+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
34+
Vite+ Task RunnerExecution Summary
35+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
36+
37+
Statistics: 1 tasks0 cache hits1 cache misses
38+
Performance: 0% cache hit rate
39+
40+
Task Details:
41+
────────────────────────────────────────────────
42+
[1] ctrl-c-interruption#interrupt-test: $ node interrupt-test.js
43+
Cache miss: no previous cache entry found
44+
Cache not updated: execution interrupted
45+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

crates/vite_task_bin/tests/e2e_snapshots/main.rs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use std::{
1111

1212
use copy_dir::copy_dir;
1313
use redact::redact_e2e_output;
14+
use send_ctrlc::{Interruptible as _, InterruptibleCommand as _};
1415
use tokio::{
1516
io::{AsyncReadExt, AsyncWriteExt},
1617
process::Command,
@@ -193,7 +194,7 @@ async fn run_case_inner(tmpdir: &AbsolutePath, fixture_path: &Path, fixture_name
193194
cmd.stdout(Stdio::piped());
194195
cmd.stderr(Stdio::piped());
195196

196-
let mut child = cmd.spawn().unwrap();
197+
let mut child = cmd.spawn_interruptible().unwrap();
197198

198199
// Write stdin if provided, then close it
199200
if let Some(stdin_content) = step.stdin() {
@@ -210,6 +211,10 @@ async fn run_case_inner(tmpdir: &AbsolutePath, fixture_path: &Path, fixture_name
210211
let mut stdout_buf = Vec::new();
211212
let mut stderr_buf = Vec::new();
212213

214+
// Magic string detection
215+
const MAGIC_STRING: &[u8] = b"[send-me-ctrl-c]";
216+
let mut ctrl_c_sent = false;
217+
213218
// Read chunks concurrently with process wait, using select! with timeout
214219
let mut stdout_done = false;
215220
let mut stderr_done = false;
@@ -232,7 +237,16 @@ async fn run_case_inner(tmpdir: &AbsolutePath, fixture_path: &Path, fixture_name
232237
result = stdout_handle.read(&mut stdout_chunk), if !stdout_done => {
233238
match result {
234239
Ok(0) => stdout_done = true,
235-
Ok(n) => stdout_buf.extend_from_slice(&stdout_chunk[..n]),
240+
Ok(n) => {
241+
let chunk = &stdout_chunk[..n];
242+
stdout_buf.extend_from_slice(chunk);
243+
244+
// Check if accumulated stdout buffer contains magic string
245+
if !ctrl_c_sent && stdout_buf.windows(MAGIC_STRING.len()).any(|w| w == MAGIC_STRING) {
246+
ctrl_c_sent = true;
247+
let _ = child.interrupt();
248+
}
249+
}
236250
Err(_) => stdout_done = true,
237251
}
238252
}

0 commit comments

Comments
 (0)