@@ -19,23 +19,23 @@ use stack_graphs::stitching::Database;
1919use stack_graphs:: stitching:: ForwardPartialPathStitcher ;
2020use std:: path:: Path ;
2121use std:: path:: PathBuf ;
22+ use std:: time:: Duration ;
2223use tree_sitter_graph:: Variables ;
2324
25+ use crate :: cli:: util:: duration_from_seconds_str;
26+ use crate :: cli:: util:: iter_files_and_directories;
27+ use crate :: cli:: util:: ConsoleFileLogger ;
2428use crate :: cli:: util:: ExistingPathBufValueParser ;
29+ use crate :: cli:: util:: FileLogger ;
2530use crate :: cli:: util:: PathSpec ;
2631use crate :: loader:: ContentProvider ;
2732use crate :: loader:: FileReader ;
2833use crate :: loader:: LanguageConfiguration ;
2934use crate :: loader:: Loader ;
3035use crate :: test:: Test ;
3136use crate :: test:: TestResult ;
37+ use crate :: CancelAfterDuration ;
3238use crate :: CancellationFlag ;
33- use crate :: NoCancellation ;
34-
35- use super :: util:: iter_files_and_directories;
36- use super :: util:: BuildErrorWithSource ;
37- use super :: util:: ConsoleFileLogger ;
38- use super :: util:: FileLogger ;
3939
4040#[ derive( Args ) ]
4141#[ clap( after_help = r#"PATH SPECIFICATIONS:
@@ -128,6 +128,14 @@ pub struct TestArgs {
128128 /// Do not load builtins for tests.
129129 #[ clap( long) ]
130130 pub no_builtins : bool ,
131+
132+ /// Maximum runtime per test in seconds.
133+ #[ clap(
134+ long,
135+ value_name = "SECONDS" ,
136+ value_parser = duration_from_seconds_str,
137+ ) ]
138+ pub max_test_time : Option < Duration > ,
131139}
132140
133141/// Flag to control output
@@ -158,6 +166,7 @@ impl TestArgs {
158166 save_visualization : None ,
159167 output_mode : OutputMode :: OnFailure ,
160168 no_builtins : false ,
169+ max_test_time : None ,
161170 }
162171 }
163172
@@ -198,7 +207,7 @@ impl TestArgs {
198207 loader : & mut Loader ,
199208 file_status : & mut ConsoleFileLogger ,
200209 ) -> anyhow:: Result < TestResult > {
201- let cancellation_flag = & NoCancellation ;
210+ let cancellation_flag = CancelAfterDuration :: from_option ( self . max_test_time ) ;
202211
203212 // If the file is skipped (ending in .skip) we construct the non-skipped path to see if we would support it.
204213 let load_path = if test_path. extension ( ) . map_or ( false , |e| e == "skip" ) {
@@ -208,7 +217,7 @@ impl TestArgs {
208217 } ;
209218 let mut file_reader = MappingFileReader :: new ( & load_path, test_path) ;
210219 let lc = match loader
211- . load_for_file ( & load_path, & mut file_reader, cancellation_flag) ?
220+ . load_for_file ( & load_path, & mut file_reader, cancellation_flag. as_ref ( ) ) ?
212221 . primary
213222 {
214223 Some ( lc) => lc,
@@ -250,36 +259,21 @@ impl TestArgs {
250259 & test_fragment. source ,
251260 & mut all_paths,
252261 & test_fragment. globals ,
253- cancellation_flag,
262+ cancellation_flag. as_ref ( ) ,
254263 )
255- . map_err ( |inner| BuildErrorWithSource {
256- inner,
257- source_path : test_path. to_path_buf ( ) ,
258- source_str : & test_fragment. source ,
259- tsg_path : PathBuf :: new ( ) ,
260- tsg_str : "" ,
261- } )
262264 } else if lc. matches_file (
263265 & test_fragment. path ,
264266 & mut Some ( test_fragment. source . as_ref ( ) ) ,
265267 ) ? {
266268 globals. clear ( ) ;
267269 test_fragment. add_globals_to ( & mut globals) ;
268- lc. sgl
269- . build_stack_graph_into (
270- & mut test. graph ,
271- test_fragment. file ,
272- & test_fragment. source ,
273- & globals,
274- cancellation_flag,
275- )
276- . map_err ( |inner| BuildErrorWithSource {
277- inner,
278- source_path : test_path. to_path_buf ( ) ,
279- source_str : & test_fragment. source ,
280- tsg_path : lc. sgl . tsg_path ( ) . to_path_buf ( ) ,
281- tsg_str : & lc. sgl . tsg_source ( ) ,
282- } )
270+ lc. sgl . build_stack_graph_into (
271+ & mut test. graph ,
272+ test_fragment. file ,
273+ & test_fragment. source ,
274+ & globals,
275+ cancellation_flag. as_ref ( ) ,
276+ )
283277 } else {
284278 return Err ( anyhow ! (
285279 "Test fragment {} not supported by language of test file {}" ,
@@ -289,7 +283,18 @@ impl TestArgs {
289283 } ;
290284 match result {
291285 Err ( err) => {
292- file_status. failure ( "failed to build stack graph" , Some ( & err. display_pretty ( ) ) ) ;
286+ file_status. failure (
287+ "failed to build stack graph" ,
288+ Some ( & format ! (
289+ "{}" ,
290+ err. display_pretty(
291+ & test. path,
292+ source,
293+ lc. sgl. tsg_path( ) ,
294+ lc. sgl. tsg_source( ) ,
295+ )
296+ ) ) ,
297+ ) ;
293298 return Err ( anyhow ! ( "Failed to build graph for {}" , test_path. display( ) ) ) ;
294299 }
295300 Ok ( _) => { }
@@ -301,13 +306,13 @@ impl TestArgs {
301306 partials. find_minimal_partial_path_set_in_file (
302307 & test. graph ,
303308 file,
304- & ( cancellation_flag as & dyn CancellationFlag ) ,
309+ & cancellation_flag. as_ref ( ) ,
305310 |g, ps, p| {
306311 db. add_partial_path ( g, ps, p) ;
307312 } ,
308313 ) ?;
309314 }
310- let result = test. run ( & mut partials, & mut db, cancellation_flag) ?;
315+ let result = test. run ( & mut partials, & mut db, cancellation_flag. as_ref ( ) ) ?;
311316 let success = self . handle_result ( & result, file_status) ?;
312317 if self . output_mode . test ( !success) {
313318 let files = test. fragments . iter ( ) . map ( |f| f. file ) . collect :: < Vec < _ > > ( ) ;
@@ -319,7 +324,7 @@ impl TestArgs {
319324 & mut db,
320325 & |_: & StackGraph , h : & Handle < File > | files. contains ( h) ,
321326 success,
322- cancellation_flag,
327+ cancellation_flag. as_ref ( ) ,
323328 ) ?;
324329 }
325330 Ok ( result)
0 commit comments