@@ -149,6 +149,16 @@ fn test_crash_tracking_bin_runtime_callback_string() {
149149 ) ;
150150}
151151
152+ #[ test]
153+ #[ cfg_attr( miri, ignore) ]
154+ fn test_crash_tracking_bin_no_runtime_callback ( ) {
155+ test_crash_tracking_bin_no_runtime_callback_impl (
156+ BuildProfile :: Release ,
157+ "donothing" ,
158+ "null_deref" ,
159+ ) ;
160+ }
161+
152162#[ test]
153163#[ cfg_attr( miri, ignore) ]
154164fn test_crash_ping_timing_and_content ( ) {
@@ -483,6 +493,23 @@ fn validate_runtime_callback_string_data(crash_payload: &Value) {
483493 }
484494}
485495
496+ fn validate_no_runtime_callback_data ( crash_payload : & Value ) {
497+ // Check if experimental section exists
498+ let experimental = crash_payload. get ( "experimental" ) ;
499+
500+ if let Some ( experimental) = experimental {
501+ // If experimental section exists, runtime_stack should not be present
502+ let runtime_stack = experimental. get ( "runtime_stack" ) ;
503+ assert ! (
504+ runtime_stack. is_none( ) ,
505+ "Runtime stack should NOT be present in experimental section when no callback is registered. Got: {:?}" ,
506+ runtime_stack
507+ ) ;
508+ }
509+ // If experimental section doesn't exist at all, that's also fine -
510+ // it means no experimental features were added to the crash report
511+ }
512+
486513fn test_crash_tracking_bin_runtime_callback_string_impl (
487514 crash_tracking_receiver_profile : BuildProfile ,
488515 mode : & str ,
@@ -562,6 +589,85 @@ fn test_crash_tracking_bin_runtime_callback_string_impl(
562589 assert_telemetry_message ( & crash_telemetry, crash_typ) ;
563590}
564591
592+ fn test_crash_tracking_bin_no_runtime_callback_impl (
593+ crash_tracking_receiver_profile : BuildProfile ,
594+ mode : & str ,
595+ crash_typ : & str ,
596+ ) {
597+ let ( crashtracker_bin, crashtracker_receiver) =
598+ setup_crashtracking_crates ( crash_tracking_receiver_profile) ;
599+ let fixtures = setup_test_fixtures ( & [ & crashtracker_receiver, & crashtracker_bin] ) ;
600+
601+ let mut p = process:: Command :: new ( & fixtures. artifacts [ & crashtracker_bin] )
602+ . arg ( format ! ( "file://{}" , fixtures. crash_profile_path. display( ) ) )
603+ . arg ( fixtures. artifacts [ & crashtracker_receiver] . as_os_str ( ) )
604+ . arg ( & fixtures. output_dir )
605+ . arg ( mode)
606+ . arg ( crash_typ)
607+ . spawn ( )
608+ . unwrap ( ) ;
609+
610+ let exit_status = bin_tests:: timeit!( "exit after signal" , {
611+ eprintln!( "Waiting for exit" ) ;
612+ p. wait( ) . unwrap( )
613+ } ) ;
614+
615+ // Should crash like normal tests
616+ assert ! ( !exit_status. success( ) ) ;
617+
618+ let stderr_path = format ! ( "{0}/out.stderr" , fixtures. output_dir. display( ) ) ;
619+ let stderr = fs:: read ( stderr_path)
620+ . context ( "reading crashtracker stderr" )
621+ . unwrap ( ) ;
622+ let stdout_path = format ! ( "{0}/out.stdout" , fixtures. output_dir. display( ) ) ;
623+ let stdout = fs:: read ( stdout_path)
624+ . context ( "reading crashtracker stdout" )
625+ . unwrap ( ) ;
626+ let s = String :: from_utf8 ( stderr) ;
627+ assert ! (
628+ matches!(
629+ s. as_deref( ) ,
630+ Ok ( "" ) | Ok ( "Failed to fully receive crash. Exit state was: StackTrace([])\n " )
631+ | Ok ( "Failed to fully receive crash. Exit state was: InternalError(\" {\\ \" ip\\ \" : \\ \" \" )\n " ) ,
632+ ) ,
633+ "got {s:?}"
634+ ) ;
635+ assert_eq ! ( Ok ( "" ) , String :: from_utf8( stdout) . as_deref( ) ) ;
636+
637+ // Check the crash data
638+ let crash_profile = fs:: read ( & fixtures. crash_profile_path )
639+ . context ( "reading crashtracker profiling payload" )
640+ . unwrap ( ) ;
641+ let crash_payload = serde_json:: from_slice :: < serde_json:: Value > ( & crash_profile)
642+ . context ( "deserializing crashtracker profiling payload to json" )
643+ . unwrap ( ) ;
644+
645+ // Validate normal crash data first
646+ assert_eq ! (
647+ serde_json:: json!( {
648+ "profiler_collecting_sample" : 1 ,
649+ "profiler_inactive" : 0 ,
650+ "profiler_serializing" : 0 ,
651+ "profiler_unwinding" : 0
652+ } ) ,
653+ crash_payload[ "counters" ] ,
654+ ) ;
655+
656+ let sig_info = & crash_payload[ "sig_info" ] ;
657+ assert_siginfo_message ( sig_info, crash_typ) ;
658+
659+ let error = & crash_payload[ "error" ] ;
660+ assert_error_message ( & error[ "message" ] , sig_info) ;
661+
662+ // Validate no runtime callback data is present
663+ validate_no_runtime_callback_data ( & crash_payload) ;
664+
665+ let crash_telemetry = fs:: read ( & fixtures. crash_telemetry_path )
666+ . context ( "reading crashtracker telemetry payload" )
667+ . unwrap ( ) ;
668+ assert_telemetry_message ( & crash_telemetry, crash_typ) ;
669+ }
670+
565671fn test_crash_tracking_bin (
566672 crash_tracking_receiver_profile : BuildProfile ,
567673 mode : & str ,
0 commit comments