@@ -2145,6 +2145,7 @@ def test_gecko_collector_with_location_info(self):
21452145 # Verify function name is in string table
21462146 self .assertIn ("handle_request" , string_array )
21472147
2148+
21482149 def test_jsonl_collector_with_location_info (self ):
21492150 """Test JsonlCollector handles LocationInfo properly."""
21502151 collapsed_out = tempfile .NamedTemporaryFile (delete = False )
@@ -2196,6 +2197,59 @@ def jsonl(obj):
21962197 self .assertIn (exp , lines )
21972198
21982199
2200+ def test_jsonl_collector_with_none_location (self ):
2201+ """Test JsonlCollector handles None location (synthetic frames)."""
2202+ collapsed_out = tempfile .NamedTemporaryFile (delete = False )
2203+ self .addCleanup (close_and_unlink , collapsed_out )
2204+
2205+ collector = JsonlCollector (sample_interval_usec = 1000 )
2206+ run_id = collector .run_id
2207+
2208+ # Create frame with None location (like GC frame)
2209+ frame = MockFrameInfo ("~" , 0 , "<GC>" )
2210+ frame .location = None # Synthetic frame has no location
2211+ frames = [
2212+ MockInterpreterInfo (
2213+ 0 ,
2214+ [MockThreadInfo (1 , [frame ], status = THREAD_STATUS_HAS_GIL )]
2215+ )
2216+ ]
2217+ collector .collect (frames )
2218+
2219+ # Should handle None location as synthetic frame
2220+ with captured_stdout (), captured_stderr ():
2221+ collector .export (collapsed_out .name )
2222+
2223+ # Check file contents
2224+ with open (collapsed_out .name , "r" ) as f :
2225+ content = f .read ()
2226+
2227+ lines = content .strip ().split ("\n " )
2228+ self .assertEqual (len (lines ), 5 )
2229+
2230+ def jsonl (obj ):
2231+ return json .dumps (obj , separators = ("," , ":" ))
2232+
2233+ expected = [
2234+ jsonl ({"type" : "meta" , "v" : 1 , "run_id" : run_id ,
2235+ "sample_interval_usec" : 1000 }),
2236+ jsonl ({"type" : "str_def" , "v" : 1 , "run_id" : run_id ,
2237+ "defs" : [{"str_id" : 1 , "value" : "<GC>" },
2238+ {"str_id" : 2 , "value" : "~" }]}),
2239+ jsonl ({"type" : "frame_def" , "v" : 1 , "run_id" : run_id ,
2240+ "defs" : [{"frame_id" : 1 , "path_str_id" : 2 , "func_str_id" : 1 ,
2241+ "line" : 0 , "synthetic" : True }]}),
2242+ jsonl ({"type" : "agg" , "v" : 1 , "run_id" : run_id ,
2243+ "kind" : "frame" , "scope" : "final" , "samples_total" : 1 ,
2244+ "entries" : [{"frame_id" : 1 , "self" : 1 , "cumulative" : 1 }]}),
2245+ jsonl ({"type" : "end" , "v" : 1 , "run_id" : run_id ,
2246+ "samples_total" : 1 }),
2247+ ]
2248+
2249+ for exp in expected :
2250+ self .assertIn (exp , lines )
2251+
2252+
21992253class TestOpcodeHandling (unittest .TestCase ):
22002254 """Tests for opcode field handling in collectors."""
22012255
0 commit comments