@@ -198,8 +198,83 @@ def test_sample_profiler_sample_method_timing(self):
198198 self .assertIn ("samples" , result )
199199
200200 # Verify collector was called multiple times
201- self .assertGreaterEqual (mock_collector .collect .call_count , 5 )
202- self .assertLessEqual (mock_collector .collect .call_count , 11 )
201+ total_weight = sum (
202+ len (c .kwargs .get ("timestamps_us" ) or [None ])
203+ for c in mock_collector .collect .call_args_list
204+ )
205+ self .assertGreaterEqual (total_weight , 5 )
206+ self .assertLessEqual (total_weight , 11 )
207+
208+ def test_sample_profiler_does_not_buffer_non_aggregating_collectors (self ):
209+ """Test that non-aggregating collectors get each sample immediately."""
210+
211+ stack_frames = [mock .sentinel .stack_frames ]
212+ mock_collector = mock .MagicMock ()
213+ mock_collector .aggregating = False
214+
215+ with self ._patched_unwinder () as u :
216+ u .instance .get_stack_trace .return_value = stack_frames
217+
218+ manager = mock .Mock ()
219+ manager .attach_mock (u .instance .get_stack_trace , "unwind" )
220+ manager .attach_mock (mock_collector .collect , "collect" )
221+
222+ profiler = SampleProfiler (
223+ pid = 12345 , sample_interval_usec = 10000 , all_threads = False
224+ )
225+
226+ times = [0.0 , 0.01 , 0.011 , 0.02 , 0.03 ]
227+ with mock .patch ("time.perf_counter" , side_effect = times ):
228+ with io .StringIO () as output :
229+ with mock .patch ("sys.stdout" , output ):
230+ profiler .sample (mock_collector , duration_sec = 0.025 )
231+
232+ self .assertEqual (
233+ manager .mock_calls ,
234+ [
235+ mock .call .unwind (),
236+ mock .call .collect (stack_frames ),
237+ mock .call .unwind (),
238+ mock .call .collect (stack_frames ),
239+ ],
240+ )
241+
242+ def test_sample_profiler_flushes_aggregated_batches_at_limit (self ):
243+ """Test that aggregating collectors flush after MAX_PENDING_SAMPLES samples."""
244+
245+ stack_frames = [mock .sentinel .stack_frames ]
246+ mock_collector = mock .MagicMock ()
247+ mock_collector .aggregating = True
248+
249+ with self ._patched_unwinder () as u :
250+ u .instance .get_stack_trace .return_value = stack_frames
251+
252+ profiler = SampleProfiler (
253+ pid = 12345 , sample_interval_usec = 10000 , all_threads = False
254+ )
255+
256+ times = [
257+ 0.0 ,
258+ 0.01 , 0.011 ,
259+ 0.02 , 0.021 ,
260+ 0.03 , 0.031 ,
261+ 0.04 , 0.041 ,
262+ 0.05 , 0.051 ,
263+ ]
264+ with mock .patch ("profiling.sampling.sample.MAX_PENDING_SAMPLES" , 2 ):
265+ with mock .patch ("time.perf_counter" , side_effect = times ):
266+ with io .StringIO () as output :
267+ with mock .patch ("sys.stdout" , output ):
268+ profiler .sample (mock_collector , duration_sec = 0.045 )
269+
270+ batches = [
271+ (c .args [0 ], len (c .kwargs ["timestamps_us" ]))
272+ for c in mock_collector .collect .call_args_list
273+ ]
274+ self .assertEqual (
275+ batches ,
276+ [(stack_frames , 2 ), (stack_frames , 2 ), (stack_frames , 1 )],
277+ )
203278
204279 def test_sample_profiler_error_handling (self ):
205280 """Test that the sample method handles errors gracefully."""
0 commit comments