Skip to content

Commit 2e11784

Browse files
committed
fix: avoid blocking on e2e step timeout
1 parent 65189ba commit 2e11784

File tree

1 file changed

+91
-55
lines changed
  • crates/vite_task_bin/tests/e2e_snapshots

1 file changed

+91
-55
lines changed

crates/vite_task_bin/tests/e2e_snapshots/main.rs

Lines changed: 91 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@ mod redact;
33
use std::{
44
env::{self, join_paths, split_paths},
55
ffi::OsStr,
6-
io::Write,
6+
io::{Read, Write},
77
process::Stdio,
8-
sync::{Arc, mpsc},
9-
time::Duration,
8+
sync::{Arc, Mutex, mpsc},
9+
time::{Duration, Instant},
1010
};
1111

1212
use copy_dir::copy_dir;
@@ -207,20 +207,8 @@ enum TerminationState {
207207
TimedOut,
208208
}
209209

210-
fn kill_process(pid: u32) {
211-
let pid_arg = vite_str::format!("{pid}");
212-
213-
#[cfg(unix)]
214-
{
215-
let _ = std::process::Command::new("kill").arg("-9").arg(pid_arg.as_str()).status();
216-
}
217-
218-
#[cfg(windows)]
219-
{
220-
let _ = std::process::Command::new("taskkill")
221-
.args(["/PID", pid_arg.as_str(), "/T", "/F"])
222-
.status();
223-
}
210+
fn kill_process(child: &mut std::process::Child) {
211+
let _ = child.kill();
224212
}
225213

226214
#[expect(
@@ -334,15 +322,16 @@ fn run_case_inner(tmpdir: &AbsolutePath, fixture_path: &std::path::Path, fixture
334322
let terminal = TestTerminal::spawn(SCREEN_SIZE, cmd).unwrap();
335323
let mut killer = terminal.child_handle.clone();
336324
let interactions = step.interactions().to_vec();
325+
let output = Arc::new(Mutex::new(String::new()));
326+
let output_for_thread = Arc::clone(&output);
337327
let (tx, rx) = mpsc::channel();
338328
std::thread::spawn(move || {
339329
let mut terminal = terminal;
340-
let mut interaction_output = String::new();
341330

342331
for interaction in interactions {
343332
match interaction {
344333
Interaction::ExpectMilestone(expect) => {
345-
interaction_output.push_str(
334+
output_for_thread.lock().unwrap().push_str(
346335
vite_str::format!(
347336
"@ expect-milestone: {}\n",
348337
expect.expect_milestone
@@ -352,18 +341,19 @@ fn run_case_inner(tmpdir: &AbsolutePath, fixture_path: &std::path::Path, fixture
352341
let milestone_screen = terminal
353342
.reader
354343
.expect_milestone(expect.expect_milestone.as_str());
355-
interaction_output.push_str(&milestone_screen);
356-
interaction_output.push('\n');
344+
let mut output = output_for_thread.lock().unwrap();
345+
output.push_str(&milestone_screen);
346+
output.push('\n');
357347
}
358348
Interaction::Write(write) => {
359-
interaction_output.push_str(
349+
output_for_thread.lock().unwrap().push_str(
360350
vite_str::format!("@ write: {}\n", write.write).as_str(),
361351
);
362352
terminal.writer.write_all(write.write.as_str().as_bytes()).unwrap();
363353
terminal.writer.flush().unwrap();
364354
}
365355
Interaction::WriteLine(write_line) => {
366-
interaction_output.push_str(
356+
output_for_thread.lock().unwrap().push_str(
367357
vite_str::format!("@ write-line: {}\n", write_line.write_line)
368358
.as_str(),
369359
);
@@ -374,7 +364,7 @@ fn run_case_inner(tmpdir: &AbsolutePath, fixture_path: &std::path::Path, fixture
374364
}
375365
Interaction::WriteKey(write_key) => {
376366
let key_name = write_key.write_key.as_str();
377-
interaction_output.push_str(
367+
output_for_thread.lock().unwrap().push_str(
378368
vite_str::format!("@ write-key: {key_name}\n").as_str(),
379369
);
380370
terminal.writer.write_all(write_key.write_key.bytes()).unwrap();
@@ -386,20 +376,25 @@ fn run_case_inner(tmpdir: &AbsolutePath, fixture_path: &std::path::Path, fixture
386376
let status = terminal.reader.wait_for_exit();
387377
let screen = terminal.reader.screen_contents();
388378

389-
let mut output = interaction_output;
390-
if !output.is_empty() && !output.ends_with('\n') {
391-
output.push('\n');
379+
{
380+
let mut output = output_for_thread.lock().unwrap();
381+
if !output.is_empty() && !output.ends_with('\n') {
382+
output.push('\n');
383+
}
384+
output.push_str(&screen);
392385
}
393-
output.push_str(&screen);
394386

395-
let _ = tx.send((i64::from(status.exit_code()), output));
387+
let _ = tx.send(i64::from(status.exit_code()));
396388
});
397389

398390
match rx.recv_timeout(STEP_TIMEOUT) {
399-
Ok((exit_code, output)) => (TerminationState::Exited(exit_code), output),
391+
Ok(exit_code) => {
392+
let output = output.lock().unwrap().clone();
393+
(TerminationState::Exited(exit_code), output)
394+
}
400395
Err(mpsc::RecvTimeoutError::Timeout) => {
401396
let _ = killer.kill();
402-
let (_, output) = rx.recv().unwrap();
397+
let output = output.lock().unwrap().clone();
403398
(TerminationState::TimedOut, output)
404399
}
405400
Err(mpsc::RecvTimeoutError::Disconnected) => {
@@ -426,37 +421,78 @@ fn run_case_inner(tmpdir: &AbsolutePath, fixture_path: &std::path::Path, fixture
426421
cmd.env("PATHEXT", pathext);
427422
}
428423

429-
let child = cmd.spawn().unwrap();
430-
let pid = child.id();
431-
let (tx, rx) = mpsc::channel();
432-
std::thread::spawn(move || {
433-
let output = child.wait_with_output();
434-
let _ = tx.send(output);
424+
let mut child = cmd.spawn().unwrap();
425+
426+
let stdout_output = Arc::new(Mutex::new(Vec::<u8>::new()));
427+
let stderr_output = Arc::new(Mutex::new(Vec::<u8>::new()));
428+
429+
let stdout = child.stdout.take().unwrap();
430+
let stdout_output_for_thread = Arc::clone(&stdout_output);
431+
let stdout_thread = std::thread::spawn(move || {
432+
let mut stdout = stdout;
433+
let mut buf = [0u8; 4096];
434+
loop {
435+
match stdout.read(&mut buf) {
436+
Ok(0) => break,
437+
Ok(n) => {
438+
stdout_output_for_thread
439+
.lock()
440+
.unwrap()
441+
.extend_from_slice(&buf[..n]);
442+
}
443+
Err(err) if err.kind() == std::io::ErrorKind::Interrupted => {}
444+
Err(_) => break,
445+
}
446+
}
435447
});
436448

437-
match rx.recv_timeout(STEP_TIMEOUT) {
438-
Ok(output) => {
439-
let output = output.unwrap();
440-
let mut combined_output =
441-
String::from_utf8_lossy(&output.stdout).into_owned();
442-
combined_output.push_str(String::from_utf8_lossy(&output.stderr).as_ref());
443-
444-
let exit_code = i64::from(output.status.code().unwrap_or(1));
445-
(TerminationState::Exited(exit_code), combined_output)
449+
let stderr = child.stderr.take().unwrap();
450+
let stderr_output_for_thread = Arc::clone(&stderr_output);
451+
let stderr_thread = std::thread::spawn(move || {
452+
let mut stderr = stderr;
453+
let mut buf = [0u8; 4096];
454+
loop {
455+
match stderr.read(&mut buf) {
456+
Ok(0) => break,
457+
Ok(n) => {
458+
stderr_output_for_thread
459+
.lock()
460+
.unwrap()
461+
.extend_from_slice(&buf[..n]);
462+
}
463+
Err(err) if err.kind() == std::io::ErrorKind::Interrupted => {}
464+
Err(_) => break,
465+
}
446466
}
447-
Err(mpsc::RecvTimeoutError::Timeout) => {
448-
kill_process(pid);
449-
let output = rx.recv().unwrap().unwrap();
467+
});
450468

451-
let mut combined_output =
452-
String::from_utf8_lossy(&output.stdout).into_owned();
453-
combined_output.push_str(String::from_utf8_lossy(&output.stderr).as_ref());
469+
let snapshot_output = || {
470+
let stdout = { stdout_output.lock().unwrap().clone() };
471+
let stderr = { stderr_output.lock().unwrap().clone() };
454472

455-
(TerminationState::TimedOut, combined_output)
473+
let mut combined_output = String::from_utf8_lossy(&stdout).into_owned();
474+
combined_output.push_str(String::from_utf8_lossy(&stderr).as_ref());
475+
combined_output
476+
};
477+
478+
let start = Instant::now();
479+
loop {
480+
if let Some(status) = child.try_wait().unwrap() {
481+
let _ = stdout_thread.join();
482+
let _ = stderr_thread.join();
483+
let combined_output = snapshot_output();
484+
485+
let exit_code = i64::from(status.code().unwrap_or(1));
486+
break (TerminationState::Exited(exit_code), combined_output);
456487
}
457-
Err(mpsc::RecvTimeoutError::Disconnected) => {
458-
panic!("Process wait thread panicked");
488+
489+
if start.elapsed() >= STEP_TIMEOUT {
490+
kill_process(&mut child);
491+
let combined_output = snapshot_output();
492+
break (TerminationState::TimedOut, combined_output);
459493
}
494+
495+
std::thread::park_timeout(Duration::from_millis(10));
460496
}
461497
};
462498

0 commit comments

Comments
 (0)