@@ -72,14 +72,8 @@ impl Run for TestCommand {
7272                    self . log_action_context ( "output" ,  & testcase. output_file . display ( ) ) ; 
7373                    testcase. build_lib ( manifest) ; 
7474                } 
75-                 TestType :: Runtime  => { 
76-                     self . log_action_start ( "TEST Runtime" ,  & testcase. name ) ; 
77-                     self . log_action_context ( "source" ,  & testcase. source . display ( ) ) ; 
78-                     self . log_action_context ( "output" ,  & testcase. output_file . display ( ) ) ; 
79-                     testcase. build ( manifest) ; 
80-                     self . run_and_check_output ( & testcase) ; 
81-                 } 
8275            } 
76+             self . check_and_run_directives ( & testcase) ; 
8377        } 
8478    } 
8579
@@ -123,20 +117,6 @@ impl TestCommand {
123117            cases. push ( testcase) ; 
124118        } 
125119
126-         // Runtime tests 
127-         for  case in  glob ( "tests/runit/*.rs" ) . unwrap ( )  { 
128-             let  case = case. unwrap ( ) ; 
129-             let  filename = case. file_stem ( ) . unwrap ( ) ; 
130-             // Skip the test runner 
131-             if  filename == "runner"  { 
132-                 continue ; 
133-             } 
134-             let  name = format ! ( "runit/{}" ,  filename. to_string_lossy( ) ) ; 
135-             let  output_file = manifest. out_dir . join ( "tests/runit" ) . join ( filename) ; 
136-             let  testcase = TestCase :: new ( name,  case,  output_file,  TestType :: Runtime ,  verbose) ; 
137-             cases. push ( testcase) ; 
138-         } 
139- 
140120        // Collect and process auxiliary builds from directives 
141121        let  mut  auxiliaries = vec ! [ ] ; 
142122        for  case in  cases. iter ( )  { 
@@ -197,11 +177,48 @@ impl TestCommand {
197177    } 
198178
199179    /// Run a runtime test and check its output against directives 
200-      fn  run_and_check_output ( & self ,  testcase :  & TestCase )  { 
180+      fn  check_and_run_directives ( & self ,  testcase :  & TestCase )  { 
201181        // Parse directives from source 
202182        let  directives = testcase. parse_directives ( ) ; 
203183        self . log_action_context ( "directives" ,  & format ! ( "found {} directives" ,  directives. len( ) ) ) ; 
204184
185+         let  mut  runpass = false ; 
186+         let  mut  exitcode = None ; 
187+         let  mut  stdout = None ; 
188+         let  mut  stderr = None ; 
189+ 
190+         // Check each directive 
191+         for  directive in  directives { 
192+             match  directive { 
193+                 TestDirective :: RunPass  => runpass = true , 
194+                 TestDirective :: CheckStdout ( expected)  => stdout = Some ( expected) , 
195+                 TestDirective :: CheckStderr ( expected)  => stderr = Some ( expected) , 
196+                 TestDirective :: ExitCode ( expected)  => exitcode = Some ( expected) , 
197+                 TestDirective :: AuxBuild ( _)  => { 
198+                     // AuxBuild directives are handled during test collection 
199+                     // No need to check them during test execution 
200+                 } 
201+             } 
202+         } 
203+ 
204+         if  !runpass && ( exitcode. is_some ( )  | stdout. is_some ( )  | stderr. is_some ( ) )  { 
205+             panic ! ( "Directives conflicts, lack of '//@ run-pass'" ) ; 
206+         } 
207+ 
208+         if  runpass { 
209+             self . run_and_check_output ( testcase,  exitcode,  stdout,  stderr) ; 
210+         } 
211+ 
212+         self . log_action_context ( "result" ,  "all checks passed" ) ; 
213+     } 
214+ 
215+     fn  run_and_check_output ( 
216+         & self , 
217+         testcase :  & TestCase , 
218+         expected_exit :  Option < i32 > , 
219+         expected_stdout :  Option < String > , 
220+         expected_stderr :  Option < String > , 
221+     )  { 
205222        // Run the test 
206223        self . log_action_context ( "running" ,  & testcase. output_file . display ( ) ) ; 
207224        let  output = std:: process:: Command :: new ( & testcase. output_file ) 
@@ -215,63 +232,53 @@ impl TestCommand {
215232        let  actual_stdout = String :: from_utf8_lossy ( & output. stdout ) . into_owned ( ) ; 
216233        let  actual_stderr = String :: from_utf8_lossy ( & output. stderr ) . into_owned ( ) ; 
217234
218-         // Check each directive 
219-         for  directive in  directives { 
220-             match  directive { 
221-                 TestDirective :: CheckStdout ( expected)  => { 
222-                     self . log_action_context ( "checking stdout" ,  & expected) ; 
223-                     let  diff = TextDiff :: from_lines ( & expected,  & actual_stdout) ; 
224-                     if  diff. ratio ( )  < 1.0  { 
225-                         cprintln ! ( "<r,s>stdout does not match expected output</r,s>" ) ; 
226-                         for  change in  diff. iter_all_changes ( )  { 
227-                             let  lineno =
228-                                 change. old_index ( ) . unwrap_or ( change. new_index ( ) . unwrap_or ( 0 ) ) ; 
229-                             match  change. tag ( )  { 
230-                                 ChangeTag :: Equal  => print ! ( " {:4}| {}" ,  lineno,  change) , 
231-                                 ChangeTag :: Insert  => cprint ! ( "<g>+{:4}| {}</g>" ,  lineno,  change) , 
232-                                 ChangeTag :: Delete  => cprint ! ( "<r>-{:4}| {}</r>" ,  lineno,  change) , 
233-                             } 
234-                         } 
235-                         std:: process:: exit ( 1 ) ; 
236-                     } 
237-                     self . log_action_context ( "stdout" ,  "passed" ) ; 
238-                 } 
239-                 TestDirective :: CheckStderr ( expected)  => { 
240-                     self . log_action_context ( "checking stderr" ,  & expected) ; 
241-                     let  diff = TextDiff :: from_lines ( & expected,  & actual_stderr) ; 
242-                     if  diff. ratio ( )  < 1.0  { 
243-                         cprintln ! ( "<r,s>stderr does not match expected output</r,s>" ) ; 
244-                         for  change in  diff. iter_all_changes ( )  { 
245-                             let  lineno =
246-                                 change. old_index ( ) . unwrap_or ( change. new_index ( ) . unwrap_or ( 0 ) ) ; 
247-                             match  change. tag ( )  { 
248-                                 ChangeTag :: Equal  => print ! ( " {:4}| {}" ,  lineno,  change) , 
249-                                 ChangeTag :: Insert  => cprint ! ( "<g>+{:4}| {}</g>" ,  lineno,  change) , 
250-                                 ChangeTag :: Delete  => cprint ! ( "<r>-{:4}| {}</r>" ,  lineno,  change) , 
251-                             } 
252-                         } 
253-                         std:: process:: exit ( 1 ) ; 
235+         { 
236+             let  expected_exit = expected_exit. unwrap_or ( 0 ) ; 
237+             self . log_action_context ( "checking exit code" ,  & expected_exit. to_string ( ) ) ; 
238+             if  actual_return != expected_exit { 
239+                 cprintln ! ( "<r,s>exit code does not match expected value</r,s>" ) ; 
240+                 cprintln ! ( "expected: {}" ,  expected_exit) ; 
241+                 cprintln ! ( "actual: {}" ,  actual_return) ; 
242+                 std:: process:: exit ( 1 ) ; 
243+             } 
244+             self . log_action_context ( "exit code" ,  "passed" ) ; 
245+         } 
246+ 
247+         if  let  Some ( expected_stdout)  = expected_stdout { 
248+             self . log_action_context ( "checking stdout" ,  & expected_stdout) ; 
249+             let  diff = TextDiff :: from_lines ( & expected_stdout,  & actual_stdout) ; 
250+             if  diff. ratio ( )  < 1.0  { 
251+                 cprintln ! ( "<r,s>stdout does not match expected output</r,s>" ) ; 
252+                 for  change in  diff. iter_all_changes ( )  { 
253+                     let  lineno = change. old_index ( ) . unwrap_or ( change. new_index ( ) . unwrap_or ( 0 ) ) ; 
254+                     match  change. tag ( )  { 
255+                         ChangeTag :: Equal  => print ! ( " {:4}| {}" ,  lineno,  change) , 
256+                         ChangeTag :: Insert  => cprint ! ( "<g>+{:4}| {}</g>" ,  lineno,  change) , 
257+                         ChangeTag :: Delete  => cprint ! ( "<r>-{:4}| {}</r>" ,  lineno,  change) , 
254258                    } 
255-                     self . log_action_context ( "stderr" ,  "passed" ) ; 
256259                } 
257-                 TestDirective :: ExitCode ( expected)  => { 
258-                     self . log_action_context ( "checking exit code" ,  & expected. to_string ( ) ) ; 
259-                     if  actual_return != expected { 
260-                         cprintln ! ( "<r,s>exit code does not match expected value</r,s>" ) ; 
261-                         cprintln ! ( "expected: {}" ,  expected) ; 
262-                         cprintln ! ( "actual: {}" ,  actual_return) ; 
263-                         std:: process:: exit ( 1 ) ; 
260+                 std:: process:: exit ( 1 ) ; 
261+             } 
262+             self . log_action_context ( "stdout" ,  "passed" ) ; 
263+         } 
264+ 
265+         if  let  Some ( expected_stderr)  = expected_stderr { 
266+             self . log_action_context ( "checking stderr" ,  & expected_stderr) ; 
267+             let  diff = TextDiff :: from_lines ( & expected_stderr,  & actual_stderr) ; 
268+             if  diff. ratio ( )  < 1.0  { 
269+                 cprintln ! ( "<r,s>stderr does not match expected output</r,s>" ) ; 
270+                 for  change in  diff. iter_all_changes ( )  { 
271+                     let  lineno = change. old_index ( ) . unwrap_or ( change. new_index ( ) . unwrap_or ( 0 ) ) ; 
272+                     match  change. tag ( )  { 
273+                         ChangeTag :: Equal  => print ! ( " {:4}| {}" ,  lineno,  change) , 
274+                         ChangeTag :: Insert  => cprint ! ( "<g>+{:4}| {}</g>" ,  lineno,  change) , 
275+                         ChangeTag :: Delete  => cprint ! ( "<r>-{:4}| {}</r>" ,  lineno,  change) , 
264276                    } 
265-                     self . log_action_context ( "exit code" ,  "passed" ) ; 
266-                 } 
267-                 TestDirective :: AuxBuild ( _)  => { 
268-                     // AuxBuild directives are handled during test collection 
269-                     // No need to check them during test execution 
270277                } 
278+                 std:: process:: exit ( 1 ) ; 
271279            } 
280+             self . log_action_context ( "stderr" ,  "passed" ) ; 
272281        } 
273- 
274-         self . log_action_context ( "result" ,  "all checks passed" ) ; 
275282    } 
276283} 
277284
@@ -285,8 +292,6 @@ pub enum TestType {
285292     FileCheck , 
286293    /// Bless test - the output should be the same as the last run 
287294     Bless , 
288-     /// Runtime test - compile, run and compare output 
289-      Runtime , 
290295} 
291296
292297impl  TestType  { 
@@ -296,7 +301,6 @@ impl TestType {
296301            TestType :: CompileLib  => "compile-lib" , 
297302            TestType :: FileCheck  => "filecheck" , 
298303            TestType :: Bless  => "bless" , 
299-             TestType :: Runtime  => "runtime" , 
300304        } 
301305    } 
302306} 
@@ -381,6 +385,7 @@ impl TestCase {
381385        let  mut  directives = Vec :: new ( ) ; 
382386
383387        // Regular expressions for matching directives 
388+         let  run_pass = regex:: Regex :: new ( r"^//@\s*run-pass" ) . unwrap ( ) ; 
384389        let  stdout_re = regex:: Regex :: new ( r"^//@\s*check-stdout:\s*(.*)" ) . unwrap ( ) ; 
385390        let  stderr_re = regex:: Regex :: new ( r"^//@\s*check-stderr:\s*(.*)" ) . unwrap ( ) ; 
386391        let  exit_re = regex:: Regex :: new ( r"^//@\s*exit-code:\s*(\d+)" ) . unwrap ( ) ; 
@@ -389,7 +394,9 @@ impl TestCase {
389394        let  directive_re = regex:: Regex :: new ( r"^//@\s*([^:]+)" ) . unwrap ( ) ; 
390395
391396        for  ( line_num,  line)  in  source. lines ( ) . enumerate ( )  { 
392-             if  let  Some ( cap)  = stdout_re. captures ( line)  { 
397+             if  let  Some ( _cap)  = run_pass. captures ( line)  { 
398+                 directives. push ( TestDirective :: RunPass ) ; 
399+             }  else  if  let  Some ( cap)  = stdout_re. captures ( line)  { 
393400                let  content = cap[ 1 ] . trim ( ) . to_string ( ) ; 
394401                directives. push ( TestDirective :: CheckStdout ( content) ) ; 
395402            }  else  if  let  Some ( cap)  = stderr_re. captures ( line)  { 
@@ -471,6 +478,9 @@ impl FileChecker {
471478/// Test directives that can appear in source files 
472479#[ derive( Debug ) ]  
473480enum  TestDirective  { 
481+     /// Compile and run a testcase, 
482+      /// expect a success (exit with 0) 
483+      RunPass , 
474484    /// Expected stdout content 
475485     CheckStdout ( String ) , 
476486    /// Expected stderr content 
0 commit comments