Files
StreamLens/test_button_persistence.py

166 lines
6.8 KiB
Python
Raw Permalink Normal View History

2025-07-30 23:48:32 -04:00
#!/usr/bin/env python3
"""
Test script to verify buttons don't disappear during loading
"""
import sys
import time
from pathlib import Path
# Add analyzer to path
sys.path.insert(0, str(Path(__file__).parent))
from analyzer.analysis.core import EthernetAnalyzer
from analyzer.tui.textual.widgets.filtered_flow_view import FilteredFlowView
from analyzer.models.flow_stats import FlowStats, FrameTypeStats
def test_button_persistence():
"""Test that predefined buttons remain visible even with 0 counts"""
print("Testing button persistence during loading...")
# Create analyzer with NO initial data (simulating early load state)
analyzer = EthernetAnalyzer()
flow_view = FilteredFlowView(analyzer)
# Simulate initial compose() by manually creating buttons
flow_view.frame_type_buttons = {}
flow_view.frame_type_buttons["Overview"] = "mock_overview_btn"
# Add predefined frame types with 0 counts (early loading state)
for frame_type in flow_view.predefined_frame_types:
flow_view.frame_type_buttons[frame_type] = f"mock_{frame_type}_btn"
print(f"✅ Initial buttons created: {len(flow_view.frame_type_buttons)} buttons")
# Test the condition logic without actually mounting widgets
frame_type_flow_counts = {}
for frame_type in flow_view.predefined_frame_types:
frame_type_flow_counts[frame_type] = 0 # All start with 0 counts
# Test the sorting and filtering logic
sorted_frame_types = sorted(frame_type_flow_counts.items(), key=lambda x: x[1], reverse=True)
# Test current_order logic (should include predefined types even with 0 count)
current_order = [ft for ft, _ in sorted_frame_types[:9]
if frame_type_flow_counts[ft] > 0 or ft in flow_view.predefined_frame_types]
if len(current_order) == len(flow_view.predefined_frame_types):
print("✅ All predefined frame types included in current_order despite 0 counts")
else:
print(f"❌ Only {len(current_order)} of {len(flow_view.predefined_frame_types)} predefined types in current_order")
return False
# Test should_show logic
hotkeys = ['2', '3', '4', '5', '6', '7', '8', '9', '0']
buttons_that_should_show = []
for i, (frame_type, flow_count) in enumerate(sorted_frame_types[:9]):
should_show = (flow_count > 0) or (frame_type in flow_view.predefined_frame_types)
if i < len(hotkeys) and should_show:
buttons_that_should_show.append(frame_type)
if len(buttons_that_should_show) == len(flow_view.predefined_frame_types):
print("✅ All predefined buttons should remain visible with 0 counts")
else:
print(f"❌ Only {len(buttons_that_should_show)} buttons would show, expected {len(flow_view.predefined_frame_types)}")
return False
# Now simulate data arriving (some frame types get counts)
print("\n🔄 Simulating data arrival...")
frame_type_flow_counts["CH10-Data"] = 500
frame_type_flow_counts["UDP"] = 200
frame_type_flow_counts["PTP-Sync"] = 100
# Re-test with data
sorted_frame_types = sorted(frame_type_flow_counts.items(), key=lambda x: x[1], reverse=True)
current_order_with_data = [ft for ft, _ in sorted_frame_types[:9]
if frame_type_flow_counts[ft] > 0 or ft in flow_view.predefined_frame_types]
# Should still include all predefined types
if len(current_order_with_data) == len(flow_view.predefined_frame_types):
print("✅ All predefined frame types still included after data arrives")
else:
print(f"❌ Only {len(current_order_with_data)} frame types included after data")
return False
# Check ordering - types with data should come first
high_count_types = [ft for ft, count in sorted_frame_types if count > 0]
zero_count_types = [ft for ft, count in sorted_frame_types if count == 0 and ft in flow_view.predefined_frame_types]
expected_order = high_count_types + zero_count_types
if current_order_with_data[:len(high_count_types)] == high_count_types:
print("✅ Frame types with data appear first in order")
else:
print("❌ Frame types with data not properly ordered first")
return False
return True
def test_no_button_flicker():
"""Test that buttons don't get removed and re-added unnecessarily"""
print("\n🎯 Testing button flicker prevention...")
analyzer = EthernetAnalyzer()
flow_view = FilteredFlowView(analyzer)
# Simulate having predefined buttons
original_buttons = {}
for i, frame_type in enumerate(flow_view.predefined_frame_types[:5]):
original_buttons[frame_type] = f"original_{frame_type}_btn"
flow_view.frame_type_buttons = {"Overview": "overview_btn", **original_buttons}
# Simulate frame types with some counts (same frame types as original buttons)
frame_type_flow_counts = {
"CH10-Data": 100, # Gets some data
"UDP": 50, # Gets some data
"PTP-Sync": 200, # Gets the most data (should be first)
"PTP-Signaling": 0, # Still 0 count
"PTP-FollowUp": 25 # Gets some data
}
# Test order comparison logic
sorted_frame_types = sorted(frame_type_flow_counts.items(), key=lambda x: x[1], reverse=True)
current_order = [ft for ft, _ in sorted_frame_types[:9]
if frame_type_flow_counts[ft] > 0 or ft in flow_view.predefined_frame_types]
previous_order = [ft for ft in flow_view.frame_type_buttons.keys() if ft != "Overview"]
# Orders should match for predefined types, preventing unnecessary recreation
if set(current_order) == set(previous_order):
print("✅ Button order unchanged - no unnecessary recreation")
return True
else:
print(f"❌ Order changed unnecessarily: {current_order} vs {previous_order}")
return False
if __name__ == "__main__":
print("StreamLens Button Persistence Test")
print("=" * 50)
try:
success1 = test_button_persistence()
success2 = test_no_button_flicker()
if success1 and success2:
print(f"\n🎉 BUTTON PERSISTENCE FIXES VERIFIED!")
print(f"\n📋 Summary of Fixes:")
print(f" ✅ Predefined buttons stay visible with 0 counts")
print(f" ✅ Buttons don't disappear during early loading")
print(f" ✅ No unnecessary button recreation/flicker")
print(f" ✅ Proper ordering: data-rich types first, then predefined")
print(f"\n🚀 Users will see stable buttons throughout loading!")
else:
print(f"\n❌ Some button persistence tests failed")
sys.exit(1)
except Exception as e:
print(f"\n❌ Test failed with error: {e}")
import traceback
traceback.print_exc()
sys.exit(1)