@@ -20,7 +20,7 @@ use foundry_cli::{
2020 opts:: CoreBuildArgs ,
2121 utils:: { self , LoadConfig } ,
2222} ;
23- use foundry_common:: { compile:: ProjectCompiler , evm:: EvmArgs , fs, shell} ;
23+ use foundry_common:: { cli_warn , compile:: ProjectCompiler , evm:: EvmArgs , fs, shell} ;
2424use foundry_compilers:: {
2525 artifacts:: output_selection:: OutputSelection ,
2626 compilers:: { multi:: MultiCompilerLanguage , CompilerSettings , Language } ,
@@ -67,29 +67,20 @@ pub struct TestArgs {
6767 #[ arg( value_hint = ValueHint :: FilePath ) ]
6868 pub path : Option < GlobMatcher > ,
6969
70- /// Run a test in the debugger.
71- ///
72- /// The argument passed to this flag is the **regex** of the test function signature you want
73- /// to run, and it works the same as --match-test.
74- ///
75- /// If more than one test matches your specified criteria, you must add additional filters
76- /// until only one test is found (see --match-contract and --match-path).
70+ /// Run a single test in the debugger.
7771 ///
7872 /// The matching test will be opened in the debugger regardless of the outcome of the test.
7973 ///
8074 /// If the matching test is a fuzz test, then it will open the debugger on the first failure
81- /// case.
82- /// If the fuzz test does not fail, it will open the debugger on the last fuzz case.
83- ///
84- /// For more fine-grained control of which fuzz case is run, see forge run.
85- #[ arg( long, value_name = "TEST_FUNCTION" ) ]
86- debug : Option < Regex > ,
75+ /// case. If the fuzz test does not fail, it will open the debugger on the last fuzz case.
76+ #[ arg( long, value_name = "DEPRECATED_TEST_FUNCTION_REGEX" ) ]
77+ debug : Option < Option < Regex > > ,
8778
8879 /// Generate a flamegraph for a single test. Implies `--decode-internal`.
8980 ///
9081 /// A flame graph is used to visualize which functions or operations within the smart contract
9182 /// are consuming the most gas overall in a sorted manner.
92- #[ arg( long, conflicts_with = "flamechart" ) ]
83+ #[ arg( long) ]
9384 flamegraph : bool ,
9485
9586 /// Generate a flamechart for a single test. Implies `--decode-internal`.
@@ -99,18 +90,13 @@ pub struct TestArgs {
9990 #[ arg( long, conflicts_with = "flamegraph" ) ]
10091 flamechart : bool ,
10192
102- /// Whether to identify internal functions in traces.
93+ /// Identify internal functions in traces.
10394 ///
104- /// If no argument is passed to this flag, it will trace internal functions scope and decode
105- /// stack parameters, but parameters stored in memory (such as bytes or arrays) will not be
106- /// decoded.
95+ /// This will trace internal functions and decode stack parameters.
10796 ///
108- /// To decode memory parameters, you should pass an argument with a test function name,
109- /// similarly to --debug and --match-test.
110- ///
111- /// If more than one test matches your specified criteria, you must add additional filters
112- /// until only one test is found (see --match-contract and --match-path).
113- #[ arg( long, value_name = "TEST_FUNCTION" ) ]
97+ /// Parameters stored in memory (such as bytes or arrays) are currently decoded only when a
98+ /// single function is matched, similarly to `--debug`, for performance reasons.
99+ #[ arg( long, value_name = "DEPRECATED_TEST_FUNCTION_REGEX" ) ]
114100 decode_internal : Option < Option < Regex > > ,
115101
116102 /// Print a gas report.
@@ -342,19 +328,15 @@ impl TestArgs {
342328 let env = evm_opts. evm_env ( ) . await ?;
343329
344330 // Enable internal tracing for more informative flamegraph.
345- if should_draw {
331+ if should_draw && self . decode_internal . is_none ( ) {
346332 self . decode_internal = Some ( None ) ;
347333 }
348334
349335 // Choose the internal function tracing mode, if --decode-internal is provided.
350- let decode_internal = if let Some ( maybe_fn) = self . decode_internal . as_ref ( ) {
351- if maybe_fn. is_some ( ) {
352- // If function filter is provided, we enable full tracing.
353- InternalTraceMode :: Full
354- } else {
355- // If no function filter is provided, we enable simple tracing.
356- InternalTraceMode :: Simple
357- }
336+ let decode_internal = if self . decode_internal . is_some ( ) {
337+ // If more than one function matched, we enable simple tracing.
338+ // If only one function matched, we enable full tracing. This is done in `run_tests`.
339+ InternalTraceMode :: Simple
358340 } else {
359341 InternalTraceMode :: None
360342 } ;
@@ -373,26 +355,27 @@ impl TestArgs {
373355 . alphanet ( evm_opts. alphanet )
374356 . build ( project_root, & output, env, evm_opts) ?;
375357
376- let mut maybe_override_mt = |flag, maybe_regex : Option < & Regex > | {
377- if let Some ( regex) = maybe_regex {
358+ let mut maybe_override_mt = |flag, maybe_regex : Option < & Option < Regex > > | {
359+ if let Some ( Some ( regex) ) = maybe_regex {
360+ cli_warn ! (
361+ "specifying argument for --{flag} is deprecated and will be removed in the future, \
362+ use --match-test instead"
363+ ) ;
364+
378365 let test_pattern = & mut filter. args_mut ( ) . test_pattern ;
379366 if test_pattern. is_some ( ) {
380367 eyre:: bail!(
381368 "Cannot specify both --{flag} and --match-test. \
382- Use --match-contract and --match-path to further limit the search instead."
369+ Use --match-contract and --match-path to further limit the search instead."
383370 ) ;
384371 }
385372 * test_pattern = Some ( regex. clone ( ) ) ;
386373 }
387374
388375 Ok ( ( ) )
389376 } ;
390-
391377 maybe_override_mt ( "debug" , self . debug . as_ref ( ) ) ?;
392- maybe_override_mt (
393- "decode-internal" ,
394- self . decode_internal . as_ref ( ) . and_then ( |v| v. as_ref ( ) ) ,
395- ) ?;
378+ maybe_override_mt ( "decode-internal" , self . decode_internal . as_ref ( ) ) ?;
396379
397380 let libraries = runner. libraries . clone ( ) ;
398381 let mut outcome = self . run_tests ( runner, config, verbosity, & filter, & output) . await ?;
@@ -401,18 +384,10 @@ impl TestArgs {
401384 let ( suite_name, test_name, mut test_result) =
402385 outcome. remove_first ( ) . ok_or_eyre ( "no tests were executed" ) ?;
403386
404- let arena = test_result
387+ let ( _ , arena) = test_result
405388 . traces
406389 . iter_mut ( )
407- . find_map (
408- |( kind, arena) | {
409- if * kind == TraceKind :: Execution {
410- Some ( arena)
411- } else {
412- None
413- }
414- } ,
415- )
390+ . find ( |( kind, _) | * kind == TraceKind :: Execution )
416391 . unwrap ( ) ;
417392
418393 // Decode traces.
@@ -425,6 +400,7 @@ impl TestArgs {
425400 let test_name = test_name. trim_end_matches ( "()" ) ;
426401 let file_name = format ! ( "cache/{label}_{contract}_{test_name}.svg" ) ;
427402 let file = std:: fs:: File :: create ( & file_name) . wrap_err ( "failed to create file" ) ?;
403+ let file = std:: io:: BufWriter :: new ( file) ;
428404
429405 let mut options = inferno:: flamegraph:: Options :: default ( ) ;
430406 options. title = format ! ( "{label} {contract}::{test_name}" ) ;
@@ -435,13 +411,13 @@ impl TestArgs {
435411 }
436412
437413 // Generate SVG.
438- inferno:: flamegraph:: from_lines ( & mut options, fst. iter ( ) . map ( |s| s . as_str ( ) ) , file)
414+ inferno:: flamegraph:: from_lines ( & mut options, fst. iter ( ) . map ( String :: as_str) , file)
439415 . wrap_err ( "failed to write svg" ) ?;
440416 println ! ( "\n Saved to {file_name}" ) ;
441417
442418 // Open SVG in default program.
443- if opener:: open ( & file_name) . is_err ( ) {
444- println ! ( "\n Failed to open {file_name}. Please open it manually. " ) ;
419+ if let Err ( e ) = opener:: open ( & file_name) {
420+ eprintln ! ( "\n Failed to open {file_name}; please open it manually: {e} " ) ;
445421 }
446422 }
447423
@@ -488,12 +464,7 @@ impl TestArgs {
488464 trace ! ( target: "forge::test" , "running all tests" ) ;
489465
490466 let num_filtered = runner. matching_test_functions ( filter) . count ( ) ;
491- if ( self . debug . is_some ( ) ||
492- self . decode_internal . as_ref ( ) . map_or ( false , |v| v. is_some ( ) ) ||
493- self . flamegraph ||
494- self . flamechart ) &&
495- num_filtered != 1
496- {
467+ if num_filtered != 1 && ( self . debug . is_some ( ) || self . flamegraph || self . flamechart ) {
497468 let action = if self . flamegraph {
498469 "generate a flamegraph"
499470 } else if self . flamechart {
@@ -512,6 +483,11 @@ impl TestArgs {
512483 ) ;
513484 }
514485
486+ // If exactly one test matched, we enable full tracing.
487+ if num_filtered == 1 && self . decode_internal . is_some ( ) {
488+ runner. decode_internal = InternalTraceMode :: Full ;
489+ }
490+
515491 if self . json {
516492 let results = runner. test_collect ( filter) ;
517493 println ! ( "{}" , serde_json:: to_string( & results) ?) ;
0 commit comments