self._saved_stderr = None
self._devnull = None
self._last_display_update = None
- self._max_sample_rate = 0 # Track maximum sample rate seen
- self._successful_samples = 0 # Track samples that captured frames
- self._failed_samples = 0 # Track samples that failed to capture frames
- self._display_update_interval = DISPLAY_UPDATE_INTERVAL # Instance variable for display refresh rate
+ self.max_sample_rate = 0 # Track maximum sample rate seen
+ self.successful_samples = 0 # Track samples that captured frames
+ self.failed_samples = 0 # Track samples that failed to capture frames
+ self.display_update_interval = DISPLAY_UPDATE_INTERVAL # Instance variable for display refresh rate
# Thread status statistics (bit flags)
- self._thread_status_counts = {
+ self.thread_status_counts = {
"has_gil": 0,
"on_cpu": 0,
"gil_requested": 0,
"unknown": 0,
"total": 0, # Total thread count across all samples
}
- self._gc_frame_samples = 0 # Track samples with GC frames
+ self.gc_frame_samples = 0 # Track samples with GC frames
# Interactive controls state
self.paused = False # Pause UI updates (profiling continues)
self._path_prefixes = self._get_common_path_prefixes()
# Widgets (initialized when display is available)
- self._header_widget = None
- self._table_widget = None
- self._footer_widget = None
- self._help_widget = None
+ self.header_widget = None
+ self.table_widget = None
+ self.footer_widget = None
+ self.help_widget = None
# Color mode
self._can_colorize = _colorize.can_colorize()
return prefixes
- def _simplify_path(self, filepath):
+ def simplify_path(self, filepath):
"""Simplify a file path by removing common prefixes."""
# Try to match against known prefixes
for prefix_path in self._path_prefixes:
# If no match, return the original path
return filepath
- def _process_frames(self, frames, thread_id=None):
+ def process_frames(self, frames, thread_id=None):
"""Process a single thread's frame stack.
Args:
thread_data.result[top_location]["direct_calls"] += 1
def collect_failed_sample(self):
- self._failed_samples += 1
+ self.failed_samples += 1
self.total_samples += 1
def collect(self, stack_frames):
frames = getattr(thread_info, "frame_info", None)
if frames:
- self._process_frames(frames, thread_id=thread_id)
+ self.process_frames(frames, thread_id=thread_id)
# Track thread IDs only for threads that actually have samples
if (
# Update cumulative thread status counts
for key, count in temp_status_counts.items():
- self._thread_status_counts[key] += count
+ self.thread_status_counts[key] += count
if has_gc_frame:
- self._gc_frame_samples += 1
+ self.gc_frame_samples += 1
- self._successful_samples += 1
+ self.successful_samples += 1
self.total_samples += 1
# Handle input on every sample for instant responsiveness
if (
self._last_display_update is None
or (current_time - self._last_display_update)
- >= self._display_update_interval
+ >= self.display_update_interval
):
self._update_display()
self._last_display_update = current_time
def _prepare_display_data(self, height):
"""Prepare data for display rendering."""
elapsed = self.elapsed_time
- stats_list = self._build_stats_list()
+ stats_list = self.build_stats_list()
# Calculate available space for stats
# Add extra lines for finished banner when in finished state
def _initialize_widgets(self, colors):
"""Initialize widgets with display and colors."""
- if self._header_widget is None:
+ if self.header_widget is None:
# Initialize trend tracker with colors
if self._trend_tracker is None:
self._trend_tracker = TrendTracker(colors, enabled=True)
- self._header_widget = HeaderWidget(self.display, colors, self)
- self._table_widget = TableWidget(self.display, colors, self)
- self._footer_widget = FooterWidget(self.display, colors, self)
- self._help_widget = HelpWidget(self.display, colors)
+ self.header_widget = HeaderWidget(self.display, colors, self)
+ self.table_widget = TableWidget(self.display, colors, self)
+ self.footer_widget = FooterWidget(self.display, colors, self)
+ self.help_widget = HelpWidget(self.display, colors)
def _render_display_sections(
self, height, width, elapsed, stats_list, colors
self._initialize_widgets(colors)
# Render header
- line = self._header_widget.render(
+ line = self.header_widget.render(
line, width, elapsed=elapsed, stats_list=stats_list
)
# Render table
- line = self._table_widget.render(
+ line = self.table_widget.render(
line, width, height=height, stats_list=stats_list
)
# Show help screen if requested
if self.show_help:
- self._help_widget.render(0, width, height=height)
+ self.help_widget.render(0, width, height=height)
self.display.refresh()
return
)
# Footer
- self._footer_widget.render(height - 2, width)
+ self.footer_widget.render(height - 2, width)
# Show filter input prompt if in filter input mode
if self.filter_input_mode:
- self._footer_widget.render_filter_input_prompt(
+ self.footer_widget.render_filter_input_prompt(
height - 1, width
)
"trend_stable": A_NORMAL,
}
- def _build_stats_list(self):
+ def build_stats_list(self):
"""Build and sort the statistics list."""
stats_list = []
result_source = self._get_current_result_source()
self.view_mode = "ALL"
self.current_thread_index = 0
self.total_samples = 0
- self._successful_samples = 0
- self._failed_samples = 0
- self._max_sample_rate = 0
- self._thread_status_counts = {
+ self.successful_samples = 0
+ self.failed_samples = 0
+ self.max_sample_rate = 0
+ self.thread_status_counts = {
"has_gil": 0,
"on_cpu": 0,
"gil_requested": 0,
"unknown": 0,
"total": 0,
}
- self._gc_frame_samples = 0
+ self.gc_frame_samples = 0
# Clear trend tracking
if self._trend_tracker is not None:
self._trend_tracker.clear()
elif ch == ord("+") or ch == ord("="):
# Decrease update interval (faster refresh)
- self._display_update_interval = max(
- 0.05, self._display_update_interval - 0.05
+ self.display_update_interval = max(
+ 0.05, self.display_update_interval - 0.05
) # Min 20Hz
elif ch == ord("-") or ch == ord("_"):
# Increase update interval (slower refresh)
- self._display_update_interval = min(
- 1.0, self._display_update_interval + 0.05
+ self.display_update_interval = min(
+ 1.0, self.display_update_interval + 0.05
) # Max 1Hz
elif ch == ord("c") or ch == ord("C"):
# Calculate display refresh rate
refresh_hz = (
- 1.0 / self.collector._display_update_interval if self.collector._display_update_interval > 0 else 0
+ 1.0 / self.collector.display_update_interval if self.collector.display_update_interval > 0 else 0
)
# Get current view mode and thread display
)
# Update max sample rate
- if sample_rate > self.collector._max_sample_rate:
- self.collector._max_sample_rate = sample_rate
+ if sample_rate > self.collector.max_sample_rate:
+ self.collector.max_sample_rate = sample_rate
col = 0
self.add_str(line, col, "Samples: ", curses.A_BOLD)
def draw_efficiency_bar(self, line, width):
"""Draw sample efficiency bar showing success/failure rates."""
success_pct = (
- self.collector._successful_samples
+ self.collector.successful_samples
/ max(1, self.collector.total_samples)
) * 100
failed_pct = (
- self.collector._failed_samples
+ self.collector.failed_samples
/ max(1, self.collector.total_samples)
) * 100
bar_width = min(MAX_EFFICIENCY_BAR_WIDTH, available_width)
success_fill = int(
(
- self.collector._successful_samples
+ self.collector.successful_samples
/ max(1, self.collector.total_samples)
)
* bar_width
"""Draw thread status statistics and GC information."""
# Get status counts for current view mode
thread_data = self.collector._get_current_thread_data()
- status_counts = thread_data.as_status_dict() if thread_data else self.collector._thread_status_counts
+ status_counts = thread_data.as_status_dict() if thread_data else self.collector.thread_status_counts
# Calculate percentages
total_threads = max(1, status_counts["total"])
pct_gc = (thread_data.gc_frame_samples / total_samples) * 100
else:
total_samples = max(1, self.collector.total_samples)
- pct_gc = (self.collector._gc_frame_samples / total_samples) * 100
+ pct_gc = (self.collector.gc_frame_samples / total_samples) * 100
col = 0
self.add_str(line, col, "Threads: ", curses.A_BOLD)
# File:line column
if col < width - 10:
- simplified_path = self.collector._simplify_path(filename)
+ simplified_path = self.collector.simplify_path(filename)
file_line = f"{simplified_path}:{lineno}"
remaining_width = width - col - 1
self.add_str(
if os_file:
stdlib_dir = os.path.dirname(os.path.abspath(os_file))
test_path = os.path.join(stdlib_dir, "json", "decoder.py")
- simplified = collector._simplify_path(test_path)
+ simplified = collector.simplify_path(test_path)
# Should remove the stdlib prefix
self.assertNotIn(stdlib_dir, simplified)
self.assertIn("json", simplified)
"""Test that unknown paths are returned unchanged."""
collector = LiveStatsCollector(1000)
test_path = "/some/unknown/path/file.py"
- simplified = collector._simplify_path(test_path)
+ simplified = collector.simplify_path(test_path)
self.assertEqual(simplified, test_path)
"""Test processing a single frame."""
collector = LiveStatsCollector(1000)
frames = [MockFrameInfo("test.py", 10, "test_func")]
- collector._process_frames(frames)
+ collector.process_frames(frames)
location = ("test.py", 10, "test_func")
self.assertEqual(collector.result[location]["direct_calls"], 1)
MockFrameInfo("test.py", 20, "middle_func"),
MockFrameInfo("test.py", 30, "outer_func"),
]
- collector._process_frames(frames)
+ collector.process_frames(frames)
# Top frame (inner_func) should have both direct and cumulative
inner_loc = ("test.py", 10, "inner_func")
def test_process_empty_frames(self):
"""Test processing empty frames list."""
collector = LiveStatsCollector(1000)
- collector._process_frames([])
+ collector.process_frames([])
# Should not raise an error and result should remain empty
self.assertEqual(len(collector.result), 0)
collector = LiveStatsCollector(1000)
frames = [MockFrameInfo("test.py", 10, "test_func")]
- collector._process_frames(frames)
- collector._process_frames(frames)
- collector._process_frames(frames)
+ collector.process_frames(frames)
+ collector.process_frames(frames)
+ collector.process_frames(frames)
location = ("test.py", 10, "test_func")
self.assertEqual(collector.result[location]["direct_calls"], 3)
frames = [MockFrameInfo("test.py", 10, "test_func")]
# Process frames with thread_id
- collector._process_frames(frames, thread_id=123)
+ collector.process_frames(frames, thread_id=123)
# Check aggregated result
location = ("test.py", 10, "test_func")
frames2 = [MockFrameInfo("test.py", 20, "other_func")]
# Process frames from different threads
- collector._process_frames(frames1, thread_id=123)
- collector._process_frames(frames2, thread_id=456)
+ collector.process_frames(frames1, thread_id=123)
+ collector.process_frames(frames2, thread_id=456)
# Check that both threads have their own data
self.assertIn(123, collector.per_thread_data)
location = ("test.py", 10, "test_func")
self.assertEqual(collector.result[location]["direct_calls"], 1)
- self.assertEqual(collector._successful_samples, 1)
- self.assertEqual(collector._failed_samples, 0)
+ self.assertEqual(collector.successful_samples, 1)
+ self.assertEqual(collector.failed_samples, 0)
def test_collect_with_empty_frames(self):
"""Test collect with empty frames."""
collector.collect(stack_frames)
# Empty frames still count as successful since collect() was called successfully
- self.assertEqual(collector._successful_samples, 1)
- self.assertEqual(collector._failed_samples, 0)
+ self.assertEqual(collector.successful_samples, 1)
+ self.assertEqual(collector.failed_samples, 0)
def test_collect_skip_idle_threads(self):
"""Test that idle threads are skipped when skip_idle=True."""
def test_build_stats_list(self):
"""Test that stats list is built correctly."""
- stats_list = self.collector._build_stats_list()
+ stats_list = self.collector.build_stats_list()
self.assertEqual(len(stats_list), 3)
# Check that all expected keys are present
def test_sort_by_nsamples(self):
"""Test sorting by number of samples."""
self.collector.sort_by = "nsamples"
- stats_list = self.collector._build_stats_list()
+ stats_list = self.collector.build_stats_list()
# Should be sorted by direct_calls descending
self.assertEqual(stats_list[0]["func"][2], "func1") # 100 samples
def test_sort_by_tottime(self):
"""Test sorting by total time."""
self.collector.sort_by = "tottime"
- stats_list = self.collector._build_stats_list()
+ stats_list = self.collector.build_stats_list()
# Should be sorted by total_time descending
# total_time = direct_calls * sample_interval_sec
def test_sort_by_cumtime(self):
"""Test sorting by cumulative time."""
self.collector.sort_by = "cumtime"
- stats_list = self.collector._build_stats_list()
+ stats_list = self.collector.build_stats_list()
# Should be sorted by cumulative_time descending
self.assertEqual(stats_list[0]["func"][2], "func2") # 200 cumulative
def test_sort_by_sample_pct(self):
"""Test sorting by sample percentage."""
self.collector.sort_by = "sample_pct"
- stats_list = self.collector._build_stats_list()
+ stats_list = self.collector.build_stats_list()
# Should be sorted by percentage of direct_calls
self.assertEqual(stats_list[0]["func"][2], "func1") # 33.3%
def test_sort_by_cumul_pct(self):
"""Test sorting by cumulative percentage."""
self.collector.sort_by = "cumul_pct"
- stats_list = self.collector._build_stats_list()
+ stats_list = self.collector.build_stats_list()
# Should be sorted by percentage of cumulative_calls
self.assertEqual(stats_list[0]["func"][2], "func2") # 66.7%
collector = LiveStatsCollector(1000, display=MockDisplay())
colors = collector._setup_colors()
collector._initialize_widgets(colors)
- self.assertEqual(collector._header_widget.format_uptime(45), "0m45s")
+ self.assertEqual(collector.header_widget.format_uptime(45), "0m45s")
def test_format_uptime_minutes(self):
"""Test uptime formatting for minutes."""
collector = LiveStatsCollector(1000, display=MockDisplay())
colors = collector._setup_colors()
collector._initialize_widgets(colors)
- self.assertEqual(collector._header_widget.format_uptime(125), "2m05s")
+ self.assertEqual(collector.header_widget.format_uptime(125), "2m05s")
def test_format_uptime_hours(self):
"""Test uptime formatting for hours."""
colors = collector._setup_colors()
collector._initialize_widgets(colors)
self.assertEqual(
- collector._header_widget.format_uptime(3661), "1h01m01s"
+ collector.header_widget.format_uptime(3661), "1h01m01s"
)
def test_format_uptime_large_values(self):
colors = collector._setup_colors()
collector._initialize_widgets(colors)
self.assertEqual(
- collector._header_widget.format_uptime(86400), "24h00m00s"
+ collector.header_widget.format_uptime(86400), "24h00m00s"
)
def test_format_uptime_zero(self):
collector = LiveStatsCollector(1000, display=MockDisplay())
colors = collector._setup_colors()
collector._initialize_widgets(colors)
- self.assertEqual(collector._header_widget.format_uptime(0), "0m00s")
+ self.assertEqual(collector.header_widget.format_uptime(0), "0m00s")
if __name__ == "__main__":
)
self.collector.start_time = time.perf_counter()
# Set a consistent display update interval for tests
- self.collector._display_update_interval = 0.1
+ self.collector.display_update_interval = 0.1
def tearDown(self):
"""Clean up after test."""
"""Test reset statistics functionality."""
# Add some stats
self.collector.total_samples = 100
- self.collector._successful_samples = 90
- self.collector._failed_samples = 10
+ self.collector.successful_samples = 90
+ self.collector.failed_samples = 10
self.collector.result[("test.py", 1, "func")] = {
"direct_calls": 50,
"cumulative_calls": 75,
self.collector.reset_stats()
self.assertEqual(self.collector.total_samples, 0)
- self.assertEqual(self.collector._successful_samples, 0)
- self.assertEqual(self.collector._failed_samples, 0)
+ self.assertEqual(self.collector.successful_samples, 0)
+ self.assertEqual(self.collector.failed_samples, 0)
self.assertEqual(len(self.collector.result), 0)
def test_increase_refresh_rate(self):
"""Test increasing refresh rate (faster updates)."""
- initial_interval = self.collector._display_update_interval
+ initial_interval = self.collector.display_update_interval
# Simulate '+' key press (faster = smaller interval)
self.display.simulate_input(ord("+"))
self.collector._handle_input()
- self.assertLess(self.collector._display_update_interval, initial_interval)
+ self.assertLess(self.collector.display_update_interval, initial_interval)
def test_decrease_refresh_rate(self):
"""Test decreasing refresh rate (slower updates)."""
- initial_interval = self.collector._display_update_interval
+ initial_interval = self.collector.display_update_interval
# Simulate '-' key press (slower = larger interval)
self.display.simulate_input(ord("-"))
self.collector._handle_input()
- self.assertGreater(self.collector._display_update_interval, initial_interval)
+ self.assertGreater(self.collector.display_update_interval, initial_interval)
def test_refresh_rate_minimum(self):
"""Test that refresh rate has a minimum (max speed)."""
- self.collector._display_update_interval = 0.05 # Set to minimum
+ self.collector.display_update_interval = 0.05 # Set to minimum
# Try to go faster
self.display.simulate_input(ord("+"))
self.collector._handle_input()
# Should stay at minimum
- self.assertEqual(self.collector._display_update_interval, 0.05)
+ self.assertEqual(self.collector.display_update_interval, 0.05)
def test_refresh_rate_maximum(self):
"""Test that refresh rate has a maximum (min speed)."""
- self.collector._display_update_interval = 1.0 # Set to maximum
+ self.collector.display_update_interval = 1.0 # Set to maximum
# Try to go slower
self.display.simulate_input(ord("-"))
self.collector._handle_input()
# Should stay at maximum
- self.assertEqual(self.collector._display_update_interval, 1.0)
+ self.assertEqual(self.collector.display_update_interval, 1.0)
def test_help_toggle(self):
"""Test help screen toggle."""
def test_increase_refresh_rate_with_equals(self):
"""Test increasing refresh rate with '=' key."""
- initial_interval = self.collector._display_update_interval
+ initial_interval = self.collector.display_update_interval
# Simulate '=' key press (alternative to '+')
self.display.simulate_input(ord("="))
self.collector._handle_input()
- self.assertLess(self.collector._display_update_interval, initial_interval)
+ self.assertLess(self.collector.display_update_interval, initial_interval)
def test_decrease_refresh_rate_with_underscore(self):
"""Test decreasing refresh rate with '_' key."""
- initial_interval = self.collector._display_update_interval
+ initial_interval = self.collector.display_update_interval
# Simulate '_' key press (alternative to '-')
self.display.simulate_input(ord("_"))
self.collector._handle_input()
- self.assertGreater(self.collector._display_update_interval, initial_interval)
+ self.assertGreater(self.collector.display_update_interval, initial_interval)
def test_finished_state_displays_banner(self):
"""Test that finished state shows prominent banner."""
"""Test filtering by filename pattern."""
self.collector.filter_pattern = "models"
- stats_list = self.collector._build_stats_list()
+ stats_list = self.collector.build_stats_list()
# Only models.py should be included
self.assertEqual(len(stats_list), 1)
"""Test filtering by function name."""
self.collector.filter_pattern = "render"
- stats_list = self.collector._build_stats_list()
+ stats_list = self.collector.build_stats_list()
self.assertEqual(len(stats_list), 1)
self.assertEqual(stats_list[0]["func"][2], "render")
"""Test that filtering is case-insensitive."""
self.collector.filter_pattern = "MODELS"
- stats_list = self.collector._build_stats_list()
+ stats_list = self.collector.build_stats_list()
# Should still match models.py
self.assertEqual(len(stats_list), 1)
"""Test substring filtering."""
self.collector.filter_pattern = "app/"
- stats_list = self.collector._build_stats_list()
+ stats_list = self.collector.build_stats_list()
# Should match both app files
self.assertEqual(len(stats_list), 2)
"""Test with no filter applied."""
self.collector.filter_pattern = None
- stats_list = self.collector._build_stats_list()
+ stats_list = self.collector.build_stats_list()
# All items should be included
self.assertEqual(len(stats_list), 3)
"""Test filtering by partial function name."""
self.collector.filter_pattern = "save"
- stats_list = self.collector._build_stats_list()
+ stats_list = self.collector.build_stats_list()
self.assertEqual(len(stats_list), 1)
self.assertEqual(stats_list[0]["func"][2], "save")
"""Test filtering matches filename:funcname pattern."""
self.collector.filter_pattern = "views.py:render"
- stats_list = self.collector._build_stats_list()
+ stats_list = self.collector.build_stats_list()
# Should match the combined pattern
self.assertEqual(len(stats_list), 1)
"""Test filter that matches nothing."""
self.collector.filter_pattern = "nonexistent"
- stats_list = self.collector._build_stats_list()
+ stats_list = self.collector.build_stats_list()
self.assertEqual(len(stats_list), 0)
def test_stats_list_in_all_mode(self):
"""Test that stats list uses aggregated data in ALL mode."""
- stats_list = self.collector._build_stats_list()
+ stats_list = self.collector.build_stats_list()
# Should have all 3 functions
self.assertEqual(len(stats_list), 3)
self.collector.view_mode = "PER_THREAD"
self.collector.current_thread_index = 0 # First thread (111)
- stats_list = self.collector._build_stats_list()
+ stats_list = self.collector.build_stats_list()
# Should only have func1 from thread 111
self.assertEqual(len(stats_list), 1)
# Thread 0 (111) -> func1
self.collector.current_thread_index = 0
- stats_list = self.collector._build_stats_list()
+ stats_list = self.collector.build_stats_list()
self.assertEqual(len(stats_list), 1)
self.assertEqual(stats_list[0]["func"][2], "func1")
# Thread 1 (222) -> func2
self.collector.current_thread_index = 1
- stats_list = self.collector._build_stats_list()
+ stats_list = self.collector.build_stats_list()
self.assertEqual(len(stats_list), 1)
self.assertEqual(stats_list[0]["func"][2], "func2")
# Thread 2 (333) -> func3
self.collector.current_thread_index = 2
- stats_list = self.collector._build_stats_list()
+ stats_list = self.collector.build_stats_list()
self.assertEqual(len(stats_list), 1)
self.assertEqual(stats_list[0]["func"][2], "func3")
# In ALL mode, should show mixed stats (50% on GIL, 50% off GIL)
self.assertEqual(collector.view_mode, "ALL")
- total_has_gil = collector._thread_status_counts["has_gil"]
- total_threads = collector._thread_status_counts["total"]
+ total_has_gil = collector.thread_status_counts["has_gil"]
+ total_threads = collector.thread_status_counts["total"]
self.assertEqual(total_has_gil, 10) # Only thread 111 has GIL
self.assertEqual(total_threads, 20) # 10 samples * 2 threads
# Check aggregated GC stats (ALL mode)
# 2 GC samples out of 10 total = 20%
- self.assertEqual(collector._gc_frame_samples, 2)
+ self.assertEqual(collector.gc_frame_samples, 2)
self.assertEqual(collector.total_samples, 5) # 5 collect() calls
# Check per-thread GC stats
def test_draw_methods_with_mock_display(self):
"""Test that draw methods write to mock display."""
self.collector.total_samples = 500
- self.collector._successful_samples = 450
- self.collector._failed_samples = 50
+ self.collector.successful_samples = 450
+ self.collector.failed_samples = 50
colors = self.collector._setup_colors()
self.collector._initialize_widgets(colors)
# Test individual widget methods
- line = self.collector._header_widget.draw_header_info(0, 160, 100.5)
+ line = self.collector.header_widget.draw_header_info(0, 160, 100.5)
self.assertEqual(line, 2) # Title + header info line
self.assertGreater(len(self.mock_display.buffer), 0)
# Clear buffer and test next method
self.mock_display.buffer.clear()
- line = self.collector._header_widget.draw_sample_stats(0, 160, 10.0)
+ line = self.collector.header_widget.draw_sample_stats(0, 160, 10.0)
self.assertEqual(line, 1)
self.assertGreater(len(self.mock_display.buffer), 0)
"""Test complete display rendering with realistic data."""
# Add multiple functions with different call counts
self.collector.total_samples = 1000
- self.collector._successful_samples = 950
- self.collector._failed_samples = 50
+ self.collector.successful_samples = 950
+ self.collector.failed_samples = 50
self.collector.result[("app.py", 10, "main")] = {
"direct_calls": 100,
def test_efficiency_bar_visualization(self):
"""Test that efficiency bar shows correct proportions."""
self.collector.total_samples = 100
- self.collector._successful_samples = 75
- self.collector._failed_samples = 25
+ self.collector.successful_samples = 75
+ self.collector.failed_samples = 25
colors = self.collector._setup_colors()
self.collector._initialize_widgets(colors)
- self.collector._header_widget.draw_efficiency_bar(0, 160)
+ self.collector.header_widget.draw_efficiency_bar(0, 160)
# Check that something was drawn to the display
self.assertGreater(len(self.mock_display.buffer), 0)
self.mock_display.buffer.clear()
self.collector.sort_by = sort_mode
- stats_list = self.collector._build_stats_list()
+ stats_list = self.collector.build_stats_list()
self.assertEqual(len(stats_list), 2)
# Verify sorting worked (func_b should be first for most modes)
colors = collector._setup_colors()
collector._initialize_widgets(colors)
line, show_sample_pct, show_tottime, show_cumul_pct, show_cumtime = (
- collector._table_widget.draw_column_headers(0, 70)
+ collector.table_widget.draw_column_headers(0, 70)
)
# On narrow terminal, some columns should be hidden
colors = collector._setup_colors()
collector._initialize_widgets(colors)
line, show_sample_pct, show_tottime, show_cumul_pct, show_cumtime = (
- collector._table_widget.draw_column_headers(0, 60)
+ collector.table_widget.draw_column_headers(0, 60)
)
# Very narrow should hide even more columns
colors = self.collector._setup_colors()
self.collector._initialize_widgets(colors)
- stats_list = self.collector._build_stats_list()
+ stats_list = self.collector.build_stats_list()
- self.collector._header_widget.draw_top_functions(0, 160, stats_list)
+ self.collector.header_widget.draw_top_functions(0, 160, stats_list)
# Top functions section should have written something
self.assertGreater(len(self.mock_display.buffer), 0)
colors = collector._setup_colors()
collector._initialize_widgets(colors)
- collector._header_widget.add_str(5, 10, "Test", 0)
+ collector.header_widget.add_str(5, 10, "Test", 0)
# Verify it was added to the buffer
self.assertIn((5, 10), mock_display.buffer)
}
self.collector._initialize_widgets(colors)
- line = self.collector._header_widget.draw_header_info(0, 160, 100.5)
+ line = self.collector.header_widget.draw_header_info(0, 160, 100.5)
self.assertEqual(line, 2) # Title + header info line
def test_draw_sample_stats(self):
colors = {"cyan": curses.A_BOLD, "green": curses.A_BOLD}
self.collector._initialize_widgets(colors)
- line = self.collector._header_widget.draw_sample_stats(0, 160, 10.0)
+ line = self.collector.header_widget.draw_sample_stats(0, 160, 10.0)
self.assertEqual(line, 1)
- self.assertGreater(self.collector._max_sample_rate, 0)
+ self.assertGreater(self.collector.max_sample_rate, 0)
def test_progress_bar_uses_target_rate(self):
"""Test that progress bar uses target rate instead of max rate."""
) # 10ms = 100Hz target
collector.start_time = time.perf_counter()
collector.total_samples = 500
- collector._max_sample_rate = (
+ collector.max_sample_rate = (
150 # Higher than target to test we don't use this
)
# Draw sample stats with a known elapsed time that gives us a specific sample rate
elapsed = 10.0 # 500 samples in 10 seconds = 50 samples/second
- line = collector._header_widget.draw_sample_stats(0, 160, elapsed)
+ line = collector.header_widget.draw_sample_stats(0, 160, elapsed)
# Verify display was updated
self.assertEqual(line, 1)
collector.display.buffer.clear()
# Draw with 1 second elapsed time (gives us current rate of 100Hz)
- collector._header_widget.draw_sample_stats(0, 160, 1.0)
+ collector.header_widget.draw_sample_stats(0, 160, 1.0)
# Check that the current/target format appears in the display with proper units
found_current_target_format = False
def test_draw_efficiency_bar(self):
"""Test drawing efficiency bar."""
- self.collector._successful_samples = 900
- self.collector._failed_samples = 100
+ self.collector.successful_samples = 900
+ self.collector.failed_samples = 100
self.collector.total_samples = 1000
colors = {"green": curses.A_BOLD, "red": curses.A_BOLD}
self.collector._initialize_widgets(colors)
- line = self.collector._header_widget.draw_efficiency_bar(0, 160)
+ line = self.collector.header_widget.draw_efficiency_bar(0, 160)
self.assertEqual(line, 1)
def test_draw_function_stats(self):
"total_rec_calls": 0,
}
- stats_list = self.collector._build_stats_list()
+ stats_list = self.collector.build_stats_list()
colors = {
"cyan": curses.A_BOLD,
"green": curses.A_BOLD,
}
self.collector._initialize_widgets(colors)
- line = self.collector._header_widget.draw_function_stats(
+ line = self.collector.header_widget.draw_function_stats(
0, 160, stats_list
)
self.assertEqual(line, 1)
"total_rec_calls": 0,
}
- stats_list = self.collector._build_stats_list()
+ stats_list = self.collector.build_stats_list()
colors = {
"red": curses.A_BOLD,
"yellow": curses.A_BOLD,
}
self.collector._initialize_widgets(colors)
- line = self.collector._header_widget.draw_top_functions(
+ line = self.collector.header_widget.draw_top_functions(
0, 160, stats_list
)
self.assertEqual(line, 1)
show_tottime,
show_cumul_pct,
show_cumtime,
- ) = self.collector._table_widget.draw_column_headers(0, 160)
+ ) = self.collector.table_widget.draw_column_headers(0, 160)
self.assertEqual(line, 1)
self.assertTrue(show_sample_pct)
self.assertTrue(show_tottime)
show_tottime,
show_cumul_pct,
show_cumtime,
- ) = self.collector._table_widget.draw_column_headers(0, 70)
+ ) = self.collector.table_widget.draw_column_headers(0, 70)
self.assertEqual(line, 1)
# Some columns should be hidden on narrow terminal
self.assertFalse(show_cumul_pct)
"""Test drawing footer."""
colors = self.collector._setup_colors()
self.collector._initialize_widgets(colors)
- self.collector._footer_widget.render(38, 160)
+ self.collector.footer_widget.render(38, 160)
# Should have written some content to the display buffer
self.assertGreater(len(self.mock_display.buffer), 0)
"""Test progress bar drawing."""
colors = self.collector._setup_colors()
self.collector._initialize_widgets(colors)
- bar, length = self.collector._header_widget.progress_bar.render_bar(
+ bar, length = self.collector.header_widget.progress_bar.render_bar(
50, 100, 30
)
"total_rec_calls": 0,
}
- stats_list = collector._build_stats_list()
+ stats_list = collector.build_stats_list()
self.assertEqual(len(stats_list), 1)
self.assertEqual(stats_list[0]["func"][2], long_name)
def test_update_display_normal(self):
"""Test normal update_display operation."""
self.collector.total_samples = 100
- self.collector._successful_samples = 90
- self.collector._failed_samples = 10
+ self.collector.successful_samples = 90
+ self.collector.failed_samples = 10
self.collector.result[("test.py", 10, "func")] = {
"direct_calls": 50,
"cumulative_calls": 75,