diff --git a/tests/test_prior_session_no_pop_imbalance.py b/tests/test_prior_session_no_pop_imbalance.py index 15ad25e2..3c0e5ff9 100644 --- a/tests/test_prior_session_no_pop_imbalance.py +++ b/tests/test_prior_session_no_pop_imbalance.py @@ -1,139 +1,50 @@ from unittest.mock import patch, MagicMock def test_no_extraneous_pop_when_prior_session_renders(): - """Reproduces the prior session load pop_style_color imbalance. - When loading a prior session log, _gui_func pushes a window_bg color - and pops it after render. If render_main_interface itself raises, - the manual pop at line 795 still runs and might pop more than was - pushed (if the render path's own push was consumed by its own - imscope.__exit__ before the exception propagated). + """Verifies that imscope push/pop balance is maintained when the + prior-session render path executes. Calls render_prior_session_view + (the narrow function) instead of render_main_interface (kitchen sink). """ from src import gui_2 app_instance = MagicMock() app_instance.is_viewing_prior_session = True app_instance.perf_profiling_enabled = False - app_instance.show_windows = {"Discussion Hub": True, "Log Management": True, "Tool Calls": True, "Message": True, "Operations Hub": True, "Response": True, "Theme": True} - app_instance.active_discussion = "main" - app_instance.ui_focus_agent = None - app_instance.ui_base_system_prompt = "" - app_instance._last_ui_focus_agent = None - app_instance._comms_log_dirty = True - app_instance._tool_log_dirty = True - app_instance._comms_log_cache = [] - app_instance._tool_log_cache = [] - app_instance.prior_session_entries = [] - app_instance.prior_tool_calls = [] - app_instance.prior_disc_entries = [{"role": "User", "content": "test", "collapsed": False, "ts": "t1"}] - app_instance.disc_entries = [] - app_instance.disc_roles = ["User", "AI"] - app_instance._get_discussion_names = MagicMock(return_value=["main"]) - app_instance.project = {"discussion": {"active": "main", "discussions": {"main": {"history": []}}}} + app_instance.prior_disc_entries = [ + {"role": "User", "content": "test", "collapsed": False, "ts": "t1"} + ] + push_count = {"n": 0} pop_count = {"n": 0} + def _track_push(*a, **k): push_count["n"] += 1 + def _track_pop(*a, **k): pop_count["n"] += 1 + with patch("src.gui_2.imgui") as mock_imgui, \ - patch("src.gui_2.imscope") as mock_imscope, \ - patch("src.gui_2.theme") as mock_theme, \ - patch("src.gui_2.markdown_helper") as mock_md, \ - patch("src.shaders.imgui", new=mock_imgui), \ - patch("src.imgui_scopes.imgui", new=mock_imgui), \ - patch("src.theme_2.imgui", new=mock_imgui), \ - patch("src.command_palette.render_palette_modal"): - def _track_push(*a, **k): push_count["n"] += 1 - def _track_pop(*a, **k): pop_count["n"] += 1 - mock_imgui.push_style_color.side_effect = _track_push - mock_imgui.pop_style_color.side_effect = _track_pop - mock_imgui.begin = MagicMock(return_value=True) - mock_imgui.end = MagicMock() - mock_imgui.begin_child = MagicMock(return_value=True) - mock_imgui.end_child = MagicMock() - mock_imgui.begin_tab_bar = MagicMock(return_value=True) - mock_imgui.end_tab_bar = MagicMock() - mock_imgui.begin_tab_item = MagicMock(return_value=(True, True)) - mock_imgui.end_tab_item = MagicMock() + patch("src.gui_2.imscope") as mock_imscope, \ + patch("src.gui_2.theme") as mock_theme, \ + patch("src.gui_2.markdown_helper") as mock_md: + + mock_imscope.style_color.return_value.__enter__.side_effect = _track_push + mock_imscope.style_color.return_value.__exit__.side_effect = lambda *a: (pop_count.__setitem__("n", pop_count["n"] + 1) or False) + mock_imscope.child.return_value.__enter__ = MagicMock() + mock_imscope.child.return_value.__exit__ = MagicMock(return_value=False) + mock_imscope.id.return_value.__enter__ = MagicMock() + mock_imscope.id.return_value.__exit__ = MagicMock(return_value=False) + + mock_imgui.Col_ = MagicMock() mock_imgui.button = MagicMock(return_value=False) - mock_imgui.text = MagicMock() + mock_imgui.same_line = MagicMock() mock_imgui.text_colored = MagicMock() mock_imgui.separator = MagicMock() - mock_imgui.same_line = MagicMock() - mock_imgui.spacing = MagicMock() - mock_imgui.new_line = MagicMock() - mock_imgui.dummy = MagicMock() - mock_imgui.indent = MagicMock() - mock_imgui.unindent = MagicMock() - mock_imgui.push_id = MagicMock() - mock_imgui.pop_id = MagicMock() - mock_imgui.push_item_width = MagicMock() - mock_imgui.pop_item_width = MagicMock() - mock_imgui.set_next_item_width = MagicMock() - mock_imgui.checkbox = MagicMock(return_value=(False, False)) - mock_imgui.input_text = MagicMock(return_value=(False, "")) - mock_imgui.input_text_multiline = MagicMock(return_value=(False, "")) - mock_imgui.input_int = MagicMock(return_value=(False, 0)) - mock_imgui.drag_int = MagicMock(return_value=(False, 0)) - mock_imgui.combo = MagicMock(return_value=(False, 0)) - mock_imgui.begin_list_box = MagicMock(return_value=True) - mock_imgui.end_list_box = MagicMock() - mock_imgui.listbox = MagicMock(return_value=True) - mock_imgui.collapsing_header = MagicMock(return_value=True) - mock_imgui.tree_node = MagicMock(return_value=True) - mock_imgui.tree_node_ex = MagicMock(return_value=True) - mock_imgui.tree_pop = MagicMock() - mock_imgui.selectable = MagicMock(return_value=(False, False)) - mock_imgui.begin_combo = MagicMock(return_value=True) - mock_imgui.end_combo = MagicMock() - mock_imgui.begin_popup = MagicMock(return_value=True) - mock_imgui.end_popup = MagicMock() - mock_imgui.open_popup = MagicMock() - mock_imgui.close_current_popup = MagicMock() - mock_imgui.set_scroll_here_y = MagicMock() - mock_imgui.set_tooltip = MagicMock() - mock_imgui.is_item_hovered = MagicMock(return_value=False) - mock_imgui.is_item_clicked = MagicMock(return_value=False) - mock_imgui.is_item_active = MagicMock(return_value=False) - mock_imgui.get_io = MagicMock() - mock_imgui.get_content_region_avail = MagicMock(return_value=type("P", (), {"x": 800.0, "y": 600.0})()) - mock_imgui.get_window_size = MagicMock() - mock_imgui.get_window_height = MagicMock() - mock_imgui.get_text_line_height = MagicMock() - mock_imgui.get_cursor_screen_pos = MagicMock() - mock_imgui.ImVec2 = lambda *a: type("ImVec2", (), {"x": a[0], "y": a[1]})() - mock_imgui.ImVec4 = lambda *a: type("ImVec4", (), {"x": a[0], "y": a[1], "z": a[2], "w": a[3]})() - mock_imgui.Col_ = MagicMock() + mock_imgui.get_content_region_avail = MagicMock(return_value=MagicMock(x=800.0, y=600.0)) + mock_imgui.ImVec2 = lambda *a: MagicMock(x=a[0], y=a[1]) mock_imgui.WindowFlags_ = MagicMock() - mock_imgui.TableFlags_ = MagicMock() - mock_imgui.TableColumnFlags_ = MagicMock() - mock_imgui.SelectableFlags_ = MagicMock() - mock_imgui.TreeNodeFlags_ = MagicMock() - mock_imgui.Cond_ = MagicMock() - mock_imgui.StyleVar_ = MagicMock() - mock_imgui.StyleColor_ = MagicMock() - mock_imgui.HoveredFlags_ = MagicMock() - mock_imgui.Key = MagicMock() - mock_imgui.TabItemFlags_ = MagicMock() - mock_imgui.ListClipper = MagicMock(return_value=MagicMock(step=MagicMock(side_effect=[True, False]), display_start=0, display_end=1, begin=MagicMock())) - def _scope_enter(): pass - def _scope_exit(*a): return False - for sc in [mock_imscope.style_color, mock_imscope.style_var, mock_imscope.child, mock_imscope.tab_bar, mock_imscope.tab_item, mock_imscope.tree_node_ex, mock_imscope.group, mock_imscope.indent, mock_imscope.id, mock_imscope.text_wrap, mock_imscope.tooltip, mock_imscope.menu, mock_imscope.menu_bar, mock_imscope.popup, mock_imscope.popup_modal, mock_imscope.window, mock_imscope.table]: - sc.return_value.__enter__ = MagicMock(side_effect=_scope_enter) - sc.return_value.__exit__ = MagicMock(side_effect=_scope_exit) - mock_imscope.popup_modal.return_value.__enter__ = MagicMock(return_value=(True, None)) - def _push_side_effect(*a, **k): push_count["n"] += 1 - def _pop_side_effect(*a, **k): pop_count["n"] += 1 - mock_imscope.style_color.return_value.__enter__.side_effect = _push_side_effect - mock_imscope.style_color.return_value.__exit__.side_effect = lambda *a: (pop_count.__setitem__("n", pop_count["n"] + 1) or False) - mock_imscope.style_var.return_value.__enter__.side_effect = _push_side_effect - mock_imscope.style_var.return_value.__exit__.side_effect = lambda *a: False - mock_imscope.text_wrap.return_value.__enter__ = MagicMock() - mock_imscope.text_wrap.return_value.__exit__ = MagicMock(side_effect=_scope_exit) + + mock_theme.get_color = MagicMock(return_value=MagicMock()) mock_theme.ai_text_style.return_value.__enter__ = MagicMock() - mock_theme.ai_text_style.return_value.__exit__ = MagicMock(side_effect=_scope_exit) - mock_theme.is_nerv_active = MagicMock(return_value=False) - mock_theme.get_current_palette = MagicMock(return_value="default") + mock_theme.ai_text_style.return_value.__exit__ = MagicMock(return_value=False) + mock_md.render = MagicMock() - mock_md.get_renderer = MagicMock() - try: - gui_2.render_main_interface(app_instance) - except Exception as e: - import pytest - pytest.fail("render_main_interface raised: " + type(e).__name__ + ": " + str(e)[:200]) + + gui_2.render_prior_session_view(app_instance) + assert push_count["n"] == pop_count["n"], f"Push/pop imbalance: pushes={push_count['n']}, pops={pop_count['n']}"