@@ -4,7 +4,7 @@ use std::path::PathBuf;
44use std:: process:: Command ;
55use std:: sync:: atomic:: { AtomicU64 , Ordering } ;
66use std:: thread;
7- use std:: time:: { SystemTime , UNIX_EPOCH } ;
7+ use std:: time:: { Duration , SystemTime , UNIX_EPOCH } ;
88
99/// Helper function to get the path to the WFL binary
1010///
@@ -70,34 +70,104 @@ pub fn get_unique_test_file_path(prefix: &str) -> PathBuf {
7070 ) )
7171}
7272
73+ /// Helper function to clean up temporary files with retry logic
74+ ///
75+ /// Attempts to remove a file with multiple retry attempts if the initial cleanup fails.
76+ /// This helps handle cases where the file might be temporarily locked or in use.
77+ fn cleanup_temp_file_with_retry ( file_path : & PathBuf , max_retries : u32 ) {
78+ for attempt in 0 ..=max_retries {
79+ match fs:: remove_file ( file_path) {
80+ Ok ( ( ) ) => return , // Success, we're done
81+ Err ( e) if attempt == max_retries => {
82+ eprintln ! (
83+ "Error: Failed to clean up test file {:?} after {} attempts: {}" ,
84+ file_path,
85+ max_retries + 1 ,
86+ e
87+ ) ;
88+ return ;
89+ }
90+ Err ( _) => {
91+ // Failed, but we have more retries left
92+ thread:: sleep ( Duration :: from_millis ( 50 ) ) ; // Brief pause before retry
93+ continue ;
94+ }
95+ }
96+ }
97+ }
98+
7399/// Helper function to run a WFL program and return the output
74100///
75101/// Executes the given WFL program content and returns the Command output.
76- /// Automatically handles temporary file creation and cleanup.
102+ /// Automatically handles temporary file creation, execution with timeout, and cleanup.
77103pub fn run_wfl_program ( program_content : & str , test_name : & str ) -> std:: process:: Output {
78104 let binary_path = get_wfl_binary_path ( ) ;
79105 let test_file = get_unique_test_file_path ( test_name) ;
80106
81107 // Write test program to temporary file
82108 fs:: write ( & test_file, program_content) . expect ( "Failed to write test file" ) ;
83109
84- // Execute WFL program
85- let output = Command :: new ( & binary_path)
86- . arg ( & test_file)
87- . output ( )
110+ // Execute WFL program with timeout
111+ let output = execute_with_timeout ( & binary_path, & test_file, Duration :: from_secs ( 30 ) )
88112 . expect ( "Failed to execute WFL binary" ) ;
89113
90- // Clean up temporary file
91- if let Err ( e) = fs:: remove_file ( & test_file) {
92- eprintln ! (
93- "Warning: Failed to clean up test file {:?}: {}" ,
94- test_file, e
95- ) ;
96- }
114+ // Clean up temporary file with retry logic
115+ cleanup_temp_file_with_retry ( & test_file, 3 ) ;
97116
98117 output
99118}
100119
120+ /// Helper function to execute a command with timeout
121+ ///
122+ /// Spawns the command and waits for it to complete within the specified timeout.
123+ /// Returns an error if the command doesn't complete within the timeout.
124+ fn execute_with_timeout (
125+ binary_path : & PathBuf ,
126+ test_file : & PathBuf ,
127+ timeout : Duration ,
128+ ) -> Result < std:: process:: Output , std:: io:: Error > {
129+ use std:: process:: Stdio ;
130+
131+ let mut child = Command :: new ( binary_path)
132+ . arg ( test_file)
133+ . stdout ( Stdio :: piped ( ) )
134+ . stderr ( Stdio :: piped ( ) )
135+ . stdin ( Stdio :: null ( ) )
136+ . spawn ( ) ?;
137+
138+ // Use a simple timeout approach since we don't have external dependencies
139+ let start = std:: time:: Instant :: now ( ) ;
140+ let timeout_duration = timeout;
141+
142+ loop {
143+ match child. try_wait ( ) {
144+ Ok ( Some ( _status) ) => {
145+ // Process finished, collect output
146+ let output = child. wait_with_output ( ) ?;
147+ return Ok ( output) ;
148+ }
149+ Ok ( None ) => {
150+ // Process still running, check timeout
151+ if start. elapsed ( ) > timeout_duration {
152+ // Timeout exceeded, kill the process
153+ let _ = child. kill ( ) ;
154+ let _ = child. wait ( ) ; // Clean up zombie
155+ return Err ( std:: io:: Error :: new (
156+ std:: io:: ErrorKind :: TimedOut ,
157+ format ! (
158+ "WFL program execution timed out after {:?}" ,
159+ timeout_duration
160+ ) ,
161+ ) ) ;
162+ }
163+ // Wait a bit before checking again
164+ thread:: sleep ( Duration :: from_millis ( 100 ) ) ;
165+ }
166+ Err ( e) => return Err ( e) ,
167+ }
168+ }
169+ }
170+
101171/// Helper function to assert WFL program execution was successful and contains expected output
102172///
103173/// Verifies that the program executed successfully and contains expected strings in stdout.
0 commit comments