Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/vite_task_bin/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ copy_dir = { workspace = true }
cow-utils = { workspace = true }
insta = { workspace = true, features = ["glob", "json", "redactions", "filters", "ron"] }
regex = { workspace = true }
send_ctrlc = { version = "0.6.0", features = ["tokio"] }
serde = { workspace = true, features = ["derive", "rc"] }
tempfile = { workspace = true }
toml = { workspace = true }
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Handle SIGINT gracefully
process.on('SIGINT', () => {
console.log('Received SIGINT, exiting gracefully');
process.exit(0);
});

// Print magic string to trigger Ctrl+C
console.log('[send-me-ctrl-c]');

// Keep the process alive to receive SIGINT
// (process will be interrupted by the test infrastructure)
setInterval(() => {}, 1000);
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"name": "ctrl-c-interruption",
"scripts": {
"interrupt-test": "node interrupt-test.js"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[[e2e]]
name = "interrupted task does not update cache"
steps = [
"vite run interrupt-test",
"vite run interrupt-test",
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
---
source: crates/vite_task_bin/tests/e2e_snapshots/main.rs
assertion_line: 309
expression: e2e_outputs
input_file: crates/vite_task_bin/tests/e2e_snapshots/fixtures/ctrl-c-interruption
---
> vite run interrupt-test
$ node interrupt-test.js
[send-me-ctrl-c]
Received SIGINT, exiting gracefully


━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Vite+ Task Runner • Execution Summary
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Statistics: 1 tasks • 0 cache hits • 1 cache misses
Performance: 0% cache hit rate

Task Details:
────────────────────────────────────────────────
[1] ctrl-c-interruption#interrupt-test: $ node interrupt-test.js ✓
→ Cache miss: no previous cache entry found
→ Cache not updated: execution interrupted
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

> vite run interrupt-test
$ node interrupt-test.js
[send-me-ctrl-c]
Received SIGINT, exiting gracefully


━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Vite+ Task Runner • Execution Summary
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Statistics: 1 tasks • 0 cache hits • 1 cache misses
Performance: 0% cache hit rate

Task Details:
────────────────────────────────────────────────
[1] ctrl-c-interruption#interrupt-test: $ node interrupt-test.js ✓
→ Cache miss: no previous cache entry found
→ Cache not updated: execution interrupted
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
18 changes: 16 additions & 2 deletions crates/vite_task_bin/tests/e2e_snapshots/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use std::{

use copy_dir::copy_dir;
use redact::redact_e2e_output;
use send_ctrlc::{Interruptible as _, InterruptibleCommand as _};
use tokio::{
io::{AsyncReadExt, AsyncWriteExt},
process::Command,
Expand Down Expand Up @@ -193,7 +194,7 @@ async fn run_case_inner(tmpdir: &AbsolutePath, fixture_path: &Path, fixture_name
cmd.stdout(Stdio::piped());
cmd.stderr(Stdio::piped());

let mut child = cmd.spawn().unwrap();
let mut child = cmd.spawn_interruptible().unwrap();

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

// Magic string detection
const MAGIC_STRING: &[u8] = b"[send-me-ctrl-c]";
let mut ctrl_c_sent = false;

// Read chunks concurrently with process wait, using select! with timeout
let mut stdout_done = false;
let mut stderr_done = false;
Expand All @@ -232,7 +237,16 @@ async fn run_case_inner(tmpdir: &AbsolutePath, fixture_path: &Path, fixture_name
result = stdout_handle.read(&mut stdout_chunk), if !stdout_done => {
match result {
Ok(0) => stdout_done = true,
Ok(n) => stdout_buf.extend_from_slice(&stdout_chunk[..n]),
Ok(n) => {
let chunk = &stdout_chunk[..n];
stdout_buf.extend_from_slice(chunk);

// Check if accumulated stdout buffer contains magic string
if !ctrl_c_sent && stdout_buf.windows(MAGIC_STRING.len()).any(|w| w == MAGIC_STRING) {
ctrl_c_sent = true;
let _ = child.interrupt();
}
}
Err(_) => stdout_done = true,
}
}
Expand Down
Loading