@@ -3,9 +3,8 @@ mod redact;
33use std:: {
44 env:: { self , join_paths, split_paths} ,
55 ffi:: OsStr ,
6- sync:: Arc ,
7- thread,
8- time:: { Duration , Instant } ,
6+ sync:: { Arc , mpsc} ,
7+ time:: Duration ,
98} ;
109
1110use copy_dir:: copy_dir;
@@ -56,27 +55,8 @@ fn get_shell_exe() -> std::path::PathBuf {
5655}
5756
5857#[ derive( serde:: Deserialize , Debug ) ]
59- #[ serde( untagged) ]
60- enum Step {
61- Simple ( Str ) ,
62- WithStdin { cmd : Str , stdin : Str } ,
63- }
64-
65- impl Step {
66- fn cmd ( & self ) -> & str {
67- match self {
68- Self :: Simple ( s) => s. as_str ( ) ,
69- Self :: WithStdin { cmd, .. } => cmd. as_str ( ) ,
70- }
71- }
72-
73- fn stdin ( & self ) -> Option < & str > {
74- match self {
75- Self :: Simple ( _) => None ,
76- Self :: WithStdin { stdin, .. } => Some ( stdin. as_str ( ) ) ,
77- }
78- }
79- }
58+ #[ serde( transparent) ]
59+ struct Step ( Str ) ;
8060
8161#[ derive( serde:: Deserialize , Debug ) ]
8262struct E2e {
@@ -212,7 +192,7 @@ fn run_case_inner(tmpdir: &AbsolutePath, fixture_path: &std::path::Path, fixture
212192 for step in & e2e. steps {
213193 let mut cmd = CommandBuilder :: new ( & shell_exe) ;
214194 cmd. arg ( "-c" ) ;
215- cmd. arg ( step. cmd ( ) ) ;
195+ cmd. arg ( step. 0 . as_str ( ) ) ;
216196 cmd. env_clear ( ) ;
217197 cmd. env ( "PATH" , & e2e_env_path) ;
218198 cmd. env ( "NO_COLOR" , "1" ) ;
@@ -228,33 +208,25 @@ fn run_case_inner(tmpdir: &AbsolutePath, fixture_path: &std::path::Path, fixture
228208
229209 let mut terminal = Terminal :: spawn ( SCREEN_SIZE , cmd) . unwrap ( ) ;
230210
231- // Write stdin if provided, then signal EOF with Ctrl+D
232- if let Some ( stdin_content) = step. stdin ( ) {
233- terminal. write ( stdin_content. as_bytes ( ) ) . unwrap ( ) ;
234- terminal. write ( & [ 0x04 ] ) . unwrap ( ) ; // Ctrl+D: flush line buffer
235- terminal. write ( & [ 0x04 ] ) . unwrap ( ) ; // Ctrl+D: signal EOF
236- }
237-
238- // Read to end on a separate thread with timeout via clone_killer
211+ // Read to end on a separate thread with timeout via channel
239212 let mut killer = terminal. clone_killer ( ) ;
240- let handle = thread:: spawn ( move || {
213+ let ( tx, rx) = mpsc:: channel ( ) ;
214+ std:: thread:: spawn ( move || {
241215 let status = terminal. read_to_end ( ) ;
242216 let screen = terminal. screen_contents ( ) ;
243- ( status, screen)
217+ let _ = tx . send ( ( status, screen) ) ;
244218 } ) ;
245219
246- let start = Instant :: now ( ) ;
247- let ( termination_state, screen) = loop {
248- if handle. is_finished ( ) {
249- let ( status, screen) = handle. join ( ) . unwrap ( ) ;
250- break ( TerminationState :: Exited ( status. unwrap ( ) ) , screen) ;
251- }
252- if start. elapsed ( ) >= STEP_TIMEOUT {
220+ let ( termination_state, screen) = match rx. recv_timeout ( STEP_TIMEOUT ) {
221+ Ok ( ( status, screen) ) => ( TerminationState :: Exited ( status. unwrap ( ) ) , screen) ,
222+ Err ( mpsc:: RecvTimeoutError :: Timeout ) => {
253223 let _ = killer. kill ( ) ;
254- let ( _, screen) = handle. join ( ) . unwrap ( ) ;
255- break ( TerminationState :: TimedOut , screen) ;
224+ let ( _, screen) = rx. recv ( ) . unwrap ( ) ;
225+ ( TerminationState :: TimedOut , screen)
226+ }
227+ Err ( mpsc:: RecvTimeoutError :: Disconnected ) => {
228+ panic ! ( "Terminal thread panicked" ) ;
256229 }
257- thread:: sleep ( Duration :: from_millis ( 10 ) ) ;
258230 } ;
259231
260232 // Format output
@@ -271,7 +243,7 @@ fn run_case_inner(tmpdir: &AbsolutePath, fixture_path: &std::path::Path, fixture
271243 }
272244
273245 e2e_outputs. push_str ( "> " ) ;
274- e2e_outputs. push_str ( step. cmd ( ) ) ;
246+ e2e_outputs. push_str ( step. 0 . as_str ( ) ) ;
275247 e2e_outputs. push ( '\n' ) ;
276248
277249 e2e_outputs. push_str ( & redact_e2e_output ( screen, e2e_stage_path_str) ) ;
0 commit comments