diff --git a/conductor/fix_test_suite_failures_20260516.md b/conductor/fix_test_suite_failures_20260516.md new file mode 100644 index 00000000..3ce275fc --- /dev/null +++ b/conductor/fix_test_suite_failures_20260516.md @@ -0,0 +1,53 @@ +# Plan: Fix Test Suite Regressions + +## Objective +Restore the full test suite to a passing state after the `gui_2.py` refactoring (Phase 2 & Phase 3 of Context Composition Redesign). + +## Key Files & Context +- `src/gui_2.py`: The main GUI entry point where methods were moved to module level. +- `src/hot_reloader.py`: Handles module registration, currently causing collisions in tests. +- `tests/*.py`: Multiple test files referencing deleted/moved methods. + +## Implementation Steps + +### Phase 1: Fix HotReloader Collisions +- [ ] Modify `src/gui_2.py` to check if `src.gui_2` is already registered before calling `HotReloader.register`. This allows multiple `App()` instances (common in tests) without triggering `ValueError`. +- [ ] Revert `src/hot_reloader.py` to its original state (raising `ValueError` on duplicate registration) to satisfy existing tests for that logic. + +### Phase 2: Fix Method Migration Regressions (Batch) +- [ ] Identify all test files calling `app._render_*` or checking `hasattr(App, '_render_*')`. +- [ ] Use Tier 3 workers to surgically update these tests: + - Change `app._render_XYZ()` to `gui_2.render_XYZ(app)`. + - Change `hasattr(App, '_render_XYZ')` to `hasattr(gui_2, 'render_XYZ')` (and import `gui_2`). + - Note: Some internal helpers might still be prefixed with `_` at the module level. +- [ ] Target the following specific files: + - `tests/test_ast_inspector_extended.py` + - `tests/test_discussion_takes_gui.py` + - `tests/test_gui_discussion_tabs.py` + - `tests/test_gui_fast_render.py` + - `tests/test_gui_phase4.py` + - `tests/test_gui_progress.py` + - `tests/test_gui_symbol_navigation.py` + - `tests/test_gui_synthesis.py` + - `tests/test_log_management_ui.py` + - `tests/test_mma_approval_indicators.py` + - `tests/test_mma_dashboard_streams.py` + - `tests/test_project_settings_rename.py` + - `tests/test_rag_gui_presence.py` + - `tests/test_selectable_ui.py` + - `tests/test_session_hub_merge.py` + - `tests/test_shader_live_editor.py` + - `tests/test_takes_panel.py` + - `tests/test_thinking_gui.py` + - `tests/test_token_viz.py` + - `tests/test_ui_summary_only_removal.py` + +### Phase 3: Address Specific Logic Failures +- [ ] `tests/test_py_struct_tools.py`: Investigate and fix why definitions are not found. +- [ ] `tests/test_rag_phase4_final_verify.py`: Investigate why RAG context is missing in history. +- [ ] `tests/test_rag_phase4_stress.py`: Investigate timing issues (incremental indexing duration). +- [ ] `tests/test_gui_kill_button.py`: Investigate missing "Actions" column in ticket queue. + +## Verification & Testing +- [ ] Run the full test suite using the batch script: `uv run python scripts/run_tests_batched.py`. +- [ ] Ensure all 243 test files pass. diff --git a/src/gui_2.py b/src/gui_2.py index d73b78bd..79e91bb4 100644 --- a/src/gui_2.py +++ b/src/gui_2.py @@ -119,12 +119,13 @@ class App: # --- Initialization --- self.controller.init_state() from src.hot_reloader import HotReloader, HotModule - HotReloader.register(HotModule( - name='src.gui_2', - file_path=__file__, - state_keys=['active_discussion', 'show_windows', 'ui_file_paths', 'ui_screenshot_paths', 'disc_entries', 'disc_roles'], - delegation_targets=['_render_main_interface', '_render_discussion_hub', '_render_files_and_media', '_render_ai_settings_hub', '_render_operations_hub', '_render_mma_dashboard'] - )) + if 'src.gui_2' not in HotReloader.HOT_MODULES: + HotReloader.register(HotModule( + name='src.gui_2', + file_path=__file__, + state_keys=['active_discussion', 'show_windows', 'ui_file_paths', 'ui_screenshot_paths', 'disc_entries', 'disc_roles'], + delegation_targets=['_render_main_interface', '_render_discussion_hub', '_render_files_and_media', '_render_ai_settings_hub', '_render_operations_hub', '_render_mma_dashboard'] + )) self.workspace_manager = workspace_manager.WorkspaceManager(project_root=self.controller.active_project_root) self.disc_entries = self.controller.disc_entries self.disc_roles = self.controller.disc_roles diff --git a/src/hot_reloader.py b/src/hot_reloader.py index 8adc93d3..c761eeb0 100644 --- a/src/hot_reloader.py +++ b/src/hot_reloader.py @@ -18,6 +18,8 @@ class HotReloader: @classmethod def register(cls, module: HotModule) -> None: + if module.name in cls.HOT_MODULES: + raise ValueError(f"Module {module.name} already registered") cls.HOT_MODULES[module.name] = module @classmethod diff --git a/tests/test_ast_inspector_extended.py b/tests/test_ast_inspector_extended.py index 5a349781..3d554c4d 100644 --- a/tests/test_ast_inspector_extended.py +++ b/tests/test_ast_inspector_extended.py @@ -1,6 +1,6 @@ import unittest.mock from unittest.mock import MagicMock, patch -from src.gui_2 import App +from src.gui_2 import App, render_ast_inspector_modal from src import models def test_ast_inspector_line_range_parsing(): @@ -37,7 +37,7 @@ def test_ast_inspector_line_range_parsing(): mock_imscope.style_var.return_value.__enter__.return_value = None # 4. Call the method - App._render_ast_inspector_modal(app) + render_ast_inspector_modal(app) # 5. Assertions assert len(app._cached_ast_nodes) == 2 diff --git a/tests/test_discussion_takes_gui.py b/tests/test_discussion_takes_gui.py index 117e8dc5..07565a75 100644 --- a/tests/test_discussion_takes_gui.py +++ b/tests/test_discussion_takes_gui.py @@ -1,5 +1,6 @@ import pytest from unittest.mock import MagicMock, patch, call +from src import gui_2 from src.gui_2 import App @pytest.fixture @@ -40,66 +41,66 @@ def app_instance(): def test_render_discussion_tabs(app_instance): """Verify that _render_discussion_panel uses tabs for discussions.""" with patch('src.gui_2.imgui') as mock_imgui, \ - patch('src.gui_2.imscope') as mock_imscope: - mock_imgui.collapsing_header.return_value = True - mock_imgui.begin_combo.return_value = False - mock_imgui.input_text.return_value = (False, "") - mock_imgui.input_int.return_value = (False, 0) - mock_imgui.input_text_multiline.return_value = (False, "") - mock_imgui.button.return_value = False - mock_imgui.checkbox.return_value = (False, False) - mock_imgui.begin_child.return_value = True - mock_imgui.selectable.return_value = (False, False) - mock_imgui.ListClipper.return_value.step.return_value = False - mock_imgui.begin_tab_bar.return_value = True + patch('src.gui_2.imscope') as mock_imscope: + mock_imgui.collapsing_header.return_value = True + mock_imgui.begin_combo.return_value = False + mock_imgui.input_text.return_value = (False, "") + mock_imgui.input_int.return_value = (False, 0) + mock_imgui.input_text_multiline.return_value = (False, "") + mock_imgui.button.return_value = False + mock_imgui.checkbox.return_value = (False, False) + mock_imgui.begin_child.return_value = True + mock_imgui.selectable.return_value = (False, False) + mock_imgui.ListClipper.return_value.step.return_value = False + mock_imgui.begin_tab_bar.return_value = True - mock_imscope.window.return_value.__enter__.return_value = (True, True) - mock_imscope.child.return_value.__enter__.return_value = True - mock_imscope.table.return_value.__enter__.return_value = True - mock_imscope.tree_node_ex.return_value.__enter__.return_value = True - mock_imscope.tab_item.return_value.__enter__.return_value = (True, True) - mock_imscope.style_color.return_value.__enter__.return_value = None - mock_imscope.style_var.return_value.__enter__.return_value = None + mock_imscope.window.return_value.__enter__.return_value = (True, True) + mock_imscope.child.return_value.__enter__.return_value = True + mock_imscope.table.return_value.__enter__.return_value = True + mock_imscope.tree_node_ex.return_value.__enter__.return_value = True + mock_imscope.tab_item.return_value.__enter__.return_value = (True, True) + mock_imscope.style_color.return_value.__enter__.return_value = None + mock_imscope.style_var.return_value.__enter__.return_value = None - mock_imgui.begin_tab_item.return_value = (True, True) + mock_imgui.begin_tab_item.return_value = (True, True) - app_instance._render_discussion_panel() + gui_2.render_discussion_panel(app_instance) - mock_imgui.begin_tab_bar.assert_called_with("discussion_takes_tabs") - assert mock_imscope.tab_item.call_count >= 3, f"Expected at least 3 tab items via imscope.tab_item, got {mock_imscope.tab_item.call_count}" + mock_imgui.begin_tab_bar.assert_called_with("discussion_takes_tabs") + assert mock_imscope.tab_item.call_count >= 3, f"Expected at least 3 tab items via imscope.tab_item, got {mock_imscope.tab_item.call_count}" def test_switching_discussion_via_tabs(app_instance): """Verify that clicking a tab switches the discussion.""" with patch('src.gui_2.imgui') as mock_imgui, \ - patch('src.gui_2.imscope') as mock_imscope, \ - patch('src.app_controller.AppController._switch_discussion') as mock_switch: - mock_imgui.collapsing_header.return_value = True - mock_imgui.begin_combo.return_value = False - mock_imgui.input_text.return_value = (False, "") - mock_imgui.input_int.return_value = (False, 0) - mock_imgui.input_text_multiline.return_value = (False, "") - mock_imgui.button.return_value = False - mock_imgui.checkbox.return_value = (False, False) - mock_imgui.begin_child.return_value = True - mock_imgui.selectable.return_value = (False, False) - mock_imgui.ListClipper.return_value.step.return_value = False - mock_imgui.begin_tab_bar.return_value = True + patch('src.gui_2.imscope') as mock_imscope, \ + patch('src.app_controller.AppController._switch_discussion') as mock_switch: + mock_imgui.collapsing_header.return_value = True + mock_imgui.begin_combo.return_value = False + mock_imgui.input_text.return_value = (False, "") + mock_imgui.input_int.return_value = (False, 0) + mock_imgui.input_text_multiline.return_value = (False, "") + mock_imgui.button.return_value = False + mock_imgui.checkbox.return_value = (False, False) + mock_imgui.begin_child.return_value = True + mock_imgui.selectable.return_value = (False, False) + mock_imgui.ListClipper.return_value.step.return_value = False + mock_imgui.begin_tab_bar.return_value = True - mock_imscope.window.return_value.__enter__.return_value = (True, True) - mock_imscope.child.return_value.__enter__.return_value = True - mock_imscope.table.return_value.__enter__.return_value = True - mock_imscope.tree_node_ex.return_value.__enter__.return_value = True - mock_imscope.tab_item.return_value.__enter__.return_value = (True, True) - mock_imscope.style_color.return_value.__enter__.return_value = None - mock_imscope.style_var.return_value.__enter__.return_value = None + mock_imscope.window.return_value.__enter__.return_value = (True, True) + mock_imscope.child.return_value.__enter__.return_value = True + mock_imscope.table.return_value.__enter__.return_value = True + mock_imscope.tree_node_ex.return_value.__enter__.return_value = True + mock_imscope.tab_item.return_value.__enter__.return_value = (True, True) + mock_imscope.style_color.return_value.__enter__.return_value = None + mock_imscope.style_var.return_value.__enter__.return_value = None - def begin_tab_item_side_effect(label, p_open=None, flags=None): - if "main_take_1" in label: - return (True, True) - return (False, True) + def begin_tab_item_side_effect(label, p_open=None, flags=None): + if "main_take_1" in label: + return (True, True) + return (False, True) - mock_imgui.begin_tab_item.side_effect = begin_tab_item_side_effect + mock_imgui.begin_tab_item.side_effect = begin_tab_item_side_effect - app_instance._render_discussion_panel() + gui_2.render_discussion_panel(app_instance) - assert mock_switch.called, f"Expected _switch_discussion to be called" \ No newline at end of file + assert mock_switch.called, f"Expected _switch_discussion to be called" diff --git a/tests/test_gui_discussion_tabs.py b/tests/test_gui_discussion_tabs.py index 76380fcf..66046a4c 100644 --- a/tests/test_gui_discussion_tabs.py +++ b/tests/test_gui_discussion_tabs.py @@ -1,6 +1,5 @@ import pytest from unittest.mock import patch, MagicMock, PropertyMock - from src import gui_2 @pytest.fixture @@ -27,7 +26,7 @@ def test_discussion_tabs_rendered(mock_gui): patch('src.gui_2.imscope') as mock_imscope, \ patch('src.imgui_scopes.imgui', new=mock_imgui), \ patch('src.app_controller.AppController.active_project_root', new_callable=PropertyMock, return_value='.'): - + # Setup imscope mocks mock_imscope.window.return_value.__enter__.return_value = (True, True) mock_imscope.child.return_value.__enter__.return_value = True @@ -40,7 +39,7 @@ def test_discussion_tabs_rendered(mock_gui): # We expect a combo box for base discussion mock_imgui.begin_combo.return_value = True mock_imgui.selectable.return_value = (False, False) - + # We expect a tab bar for takes mock_imgui.begin_tab_bar.return_value = True mock_imgui.begin_tab_item.return_value = (True, True) @@ -48,16 +47,16 @@ def test_discussion_tabs_rendered(mock_gui): mock_imgui.input_text_multiline.return_value = (False, "") mock_imgui.checkbox.return_value = (False, False) mock_imgui.input_int.return_value = (False, 0) - + mock_clipper = MagicMock() mock_clipper.step.return_value = False mock_imgui.ListClipper.return_value = mock_clipper - - mock_gui._render_discussion_panel() - + + gui_2.render_discussion_panel(mock_gui) + mock_imgui.begin_combo.assert_called_once_with("##disc_sel", 'main') mock_imgui.begin_tab_bar.assert_called_once_with('discussion_takes_tabs') - + calls = [c[0][0] for c in mock_imscope.tab_item.call_args_list] assert 'Original###main' in calls assert 'Take 1###main_take_1' in calls diff --git a/tests/test_gui_fast_render.py b/tests/test_gui_fast_render.py index 74bf0017..1adc32b6 100644 --- a/tests/test_gui_fast_render.py +++ b/tests/test_gui_fast_render.py @@ -1,6 +1,7 @@ import pytest from unittest.mock import patch, MagicMock from src.gui_2 import App +from src import gui_2 def test_render_context_composition_panel_fast(app_instance: App): """Verifies that the context composition panel rendering path executes without exceptions.""" @@ -18,9 +19,9 @@ def test_render_context_composition_panel_fast(app_instance: App): mock_imscope.tab_item.return_value.__enter__.return_value = (True, True) try: - app_instance._render_context_composition_panel() + gui_2.render_context_composition_panel(app_instance) except Exception as e: - pytest.fail(f"_render_context_composition_panel raised an exception: {e}") + pytest.fail(f"render_context_composition_panel raised an exception: {e}") def test_render_discussion_panel_fast(app_instance: App): """Verifies that the discussion panel rendering path executes without exceptions.""" @@ -37,10 +38,15 @@ def test_render_discussion_panel_fast(app_instance: App): mock_imgui.ListClipper.return_value.step.return_value = False mock_imscope.tab_item.return_value.__enter__.return_value = (True, True) + # Mocks for complex imgui logic in render_discussion_tab + mock_imgui.is_item_active.return_value = False + mock_imgui.begin_tab_bar.return_value = False + mock_imgui.begin_tab_item.return_value = (False, False) + try: - app_instance._render_discussion_panel() + gui_2.render_discussion_hub(app_instance) except Exception as e: - pytest.fail(f"_render_discussion_panel raised an exception: {e}") + pytest.fail(f"render_discussion_hub raised an exception: {e}") def test_render_files_and_media_fast(app_instance: App): """Verifies that the files and media panel rendering path executes without exceptions.""" @@ -58,6 +64,6 @@ def test_render_files_and_media_fast(app_instance: App): mock_imscope.tab_item.return_value.__enter__.return_value = (True, True) try: - app_instance._render_files_and_media() + gui_2.render_files_and_media(app_instance) except Exception as e: - pytest.fail(f"_render_files_and_media raised an exception: {e}") \ No newline at end of file + pytest.fail(f"render_files_and_media raised an exception: {e}") \ No newline at end of file diff --git a/tests/test_gui_phase4.py b/tests/test_gui_phase4.py index 81e46bc0..99020bf0 100644 --- a/tests/test_gui_phase4.py +++ b/tests/test_gui_phase4.py @@ -1,5 +1,6 @@ import pytest from unittest.mock import MagicMock, patch +from src import gui_2 from src.gui_2 import App from src.models import Track @@ -70,6 +71,8 @@ def test_delete_ticket_logic(mock_app: App): def test_track_discussion_toggle(mock_app: App): with ( patch('src.gui_2.imgui') as mock_imgui, + patch('src.theme_2.imgui', new=mock_imgui), + patch('src.imgui_scopes.imgui', new=mock_imgui), patch('src.gui_2.imscope') as mock_imscope, patch('src.gui_2.project_manager.load_track_history', return_value=["@2026-03-01 12:00:00\n[User]\nTrack Hello"]) as mock_load, patch('src.gui_2.project_manager.str_to_entry', side_effect=lambda s, roles: {"ts": "12:00", "role": "User", "content": s.split("\n")[-1]}), @@ -97,11 +100,19 @@ def test_track_discussion_toggle(mock_app: App): mock_imgui.begin_combo.return_value = False mock_imgui.selectable.return_value = (False, False) mock_imgui.button.return_value = False - mock_imgui.collapsing_header.return_value = True # For Discussions header + mock_imgui.combo.return_value = (False, 0) + mock_imgui.begin_tab_bar.return_value = True + mock_imgui.collapsing_header.return_value = True # For Discussions header mock_imgui.input_text.side_effect = lambda label, value, *args, **kwargs: (False, value) mock_imgui.input_text_multiline.side_effect = lambda label, value, *args, **kwargs: (False, value) mock_imgui.input_int.side_effect = lambda label, value, *args, **kwargs: (False, value) mock_imgui.begin_child.return_value = True + mock_imgui.get_window_height.return_value = 800.0 + mock_imgui.get_io.return_value.mouse_delta.y = 0.0 + mock_imgui.is_item_active.return_value = False + mock_imgui.get_style.return_value.color_.return_value = (1, 1, 1, 1) + mock_imgui.Col_ = MagicMock() + mock_imgui.set_scroll_here_y.return_value = None # Mock clipper to avoid the while loop hang mock_clipper = MagicMock() mock_clipper.step.side_effect = [True, False] @@ -109,7 +120,7 @@ def test_track_discussion_toggle(mock_app: App): mock_clipper.display_end = 0 mock_imgui.ListClipper.return_value = mock_clipper - mock_app._render_discussion_panel() + gui_2.render_discussion_hub(mock_app) assert mock_app._track_discussion_active mock_flush.assert_called() @@ -127,7 +138,7 @@ def test_track_discussion_toggle(mock_app: App): mock_imgui.checkbox.side_effect = checkbox_off_side_effect mock_clipper.step.side_effect = [True, False] # Reset clipper - mock_app._render_discussion_panel() + gui_2.render_discussion_hub(mock_app) assert not mock_app._track_discussion_active mock_switch.assert_called_with(mock_app.active_discussion) diff --git a/tests/test_gui_progress.py b/tests/test_gui_progress.py index 50e9b247..56592e29 100644 --- a/tests/test_gui_progress.py +++ b/tests/test_gui_progress.py @@ -1,5 +1,6 @@ import pytest from unittest.mock import MagicMock, patch +from src import gui_2 from src.gui_2 import App, C_LBL, C_VAL from src.models import Ticket @@ -60,16 +61,16 @@ def test_render_mma_dashboard_progress(): # Call the method # Dashboard now delegates to sub-methods, so we wire them up to execute their real logic on the mock instance. - app._render_mma_focus_selector.side_effect = lambda: App._render_mma_focus_selector(app) - app._render_mma_track_summary.side_effect = lambda: App._render_mma_track_summary(app) - app._render_mma_epic_planner.side_effect = lambda: App._render_mma_epic_planner(app) - app._render_mma_conductor_setup.side_effect = lambda: App._render_mma_conductor_setup(app) - app._render_mma_track_browser.side_effect = lambda: App._render_mma_track_browser(app) - app._render_mma_global_controls.side_effect = lambda: App._render_mma_global_controls(app) - app._render_mma_usage_section.side_effect = lambda: App._render_mma_usage_section(app) - app._render_mma_agent_streams.side_effect = lambda: App._render_mma_agent_streams(app) + app._render_mma_focus_selector.side_effect = lambda: gui_2.render_mma_focus_selector(app) + app._render_mma_track_summary.side_effect = lambda: gui_2.render_mma_track_summary(app) + app._render_mma_epic_planner.side_effect = lambda: gui_2.render_mma_epic_planner(app) + app._render_mma_conductor_setup.side_effect = lambda: gui_2.render_mma_conductor_setup(app) + app._render_mma_track_browser.side_effect = lambda: gui_2.render_mma_track_browser(app) + app._render_mma_global_controls.side_effect = lambda: gui_2.render_mma_global_controls(app) + app._render_mma_usage_section.side_effect = lambda: gui_2.render_mma_usage_section(app) + app._render_mma_agent_streams.side_effect = lambda: gui_2.render_mma_agent_streams(app) - App._render_mma_dashboard(app) + gui_2.render_mma_dashboard(app) # Assertions # 1 completed out of 4 tickets = 25.0% progress # Update assertions: imgui.progress_bar is called with (0.25, (-1.0, 0.0), '25.0%') diff --git a/tests/test_gui_symbol_navigation.py b/tests/test_gui_symbol_navigation.py index d381fa50..7b94dea3 100644 --- a/tests/test_gui_symbol_navigation.py +++ b/tests/test_gui_symbol_navigation.py @@ -1,5 +1,6 @@ import pytest from unittest.mock import MagicMock, patch +from src import gui_2 from src.gui_2 import App @pytest.mark.parametrize("role", ["User", "AI"]) @@ -75,7 +76,7 @@ def test_render_discussion_panel_symbol_lookup(mock_app, role): mock_mcp.read_file.return_value = "class MyClass:\n pass" # Execute the panel rendering - mock_app._render_discussion_panel() + gui_2.render_discussion_panel(mock_app) # Assertions # 1. Assert that the regex correctly identifies the pattern and imgui.button('[Source]##0_0') is called diff --git a/tests/test_gui_synthesis.py b/tests/test_gui_synthesis.py index 0dab9e1e..f2b74b57 100644 --- a/tests/test_gui_synthesis.py +++ b/tests/test_gui_synthesis.py @@ -1,5 +1,6 @@ import pytest from unittest.mock import MagicMock, patch, ANY +from src import gui_2 from src.gui_2 import App @pytest.fixture @@ -35,7 +36,7 @@ def app_instance(): yield app def test_render_synthesis_panel(app_instance): - """Verify that _render_synthesis_panel renders checkboxes for takes and input for prompt.""" + """Verify that render_synthesis_panel renders checkboxes for takes and input for prompt.""" with patch('src.gui_2.imgui') as mock_imgui, \ patch('src.gui_2.imscope') as mock_imscope: mock_imgui.checkbox.return_value = (False, False) @@ -52,7 +53,7 @@ def test_render_synthesis_panel(app_instance): mock_imscope.style_var.return_value.__enter__.return_value = None # Call the method we are testing - app_instance._render_synthesis_panel() + gui_2.render_synthesis_panel(app_instance) # 1. Assert imgui.checkbox is called for each take in project_dict['discussion']['discussions'] discussions = app_instance.project['discussion']['discussions'] diff --git a/tests/test_log_management_ui.py b/tests/test_log_management_ui.py index fe34b51b..43dea799 100644 --- a/tests/test_log_management_ui.py +++ b/tests/test_log_management_ui.py @@ -1,8 +1,7 @@ import pytest from unittest.mock import MagicMock, patch from pathlib import Path - -# We can safely import gui_2 if we don't instantiate App without mocking its threads +from src import gui_2 from src.gui_2 import App @pytest.fixture @@ -60,8 +59,8 @@ def test_log_management_init(app_instance: App) -> None: app = app_instance assert "Log Management" in app.show_windows assert app.show_windows["Log Management"] is False - assert hasattr(app, "_render_log_management") - assert callable(app._render_log_management) + assert hasattr(gui_2, "render_log_management") + assert callable(gui_2.render_log_management) def test_render_log_management_logic(app_instance: App) -> None: app = app_instance @@ -98,7 +97,7 @@ def test_render_log_management_logic(app_instance: App) -> None: mock_imscope.window.return_value = mock_window_cm mock_begin_table.return_value = True - app._render_log_management() + gui_2.render_log_management(app) mock_imscope.window.assert_called_with("Log Management", app.show_windows["Log Management"]) mock_begin_table.assert_called() mock_text.assert_any_call("session_1") diff --git a/tests/test_mma_approval_indicators.py b/tests/test_mma_approval_indicators.py index b80867e6..6843104d 100644 --- a/tests/test_mma_approval_indicators.py +++ b/tests/test_mma_approval_indicators.py @@ -1,6 +1,7 @@ from __future__ import annotations from unittest.mock import patch, MagicMock +from src import gui_2 from src.gui_2 import App @@ -63,18 +64,18 @@ def _make_app(**kwargs): app.controller = mock_controller # Ensure real methods are called for the dashboard rendering components - app._render_mma_dashboard = lambda: App._render_mma_dashboard(app) - app._render_mma_focus_selector = lambda: App._render_mma_focus_selector(app) - app._render_mma_track_summary = lambda: App._render_mma_track_summary(app) - app._render_mma_epic_planner = lambda: App._render_mma_epic_planner(app) - app._render_mma_conductor_setup = lambda: App._render_mma_conductor_setup(app) - app._render_mma_track_browser = lambda: App._render_mma_track_browser(app) - app._render_mma_global_controls = lambda: App._render_mma_global_controls(app) - app._render_mma_usage_section = lambda: App._render_mma_usage_section(app) - app._render_ticket_queue = lambda: App._render_ticket_queue(app) + app._render_mma_dashboard = lambda: gui_2.render_mma_dashboard(app) + app._render_mma_focus_selector = lambda: gui_2.render_mma_focus_selector(app) + app._render_mma_track_summary = lambda: gui_2.render_mma_track_summary(app) + app._render_mma_epic_planner = lambda: gui_2.render_mma_epic_planner(app) + app._render_mma_conductor_setup = lambda: gui_2.render_mma_conductor_setup(app) + app._render_mma_track_browser = lambda: gui_2.render_mma_track_browser(app) + app._render_mma_global_controls = lambda: gui_2.render_mma_global_controls(app) + app._render_mma_usage_section = lambda: gui_2.render_mma_usage_section(app) + app._render_ticket_queue = lambda: gui_2.render_ticket_queue(app) app._render_window_if_open = lambda name, func, condition=True: func() if condition else None - app._render_mma_ticket_editor = lambda: App._render_mma_ticket_editor(app) - app._render_mma_agent_streams = lambda: App._render_mma_agent_streams(app) + app._render_mma_ticket_editor = lambda: gui_2.render_mma_ticket_editor(app) + app._render_mma_agent_streams = lambda: gui_2.render_mma_agent_streams(app) return app @@ -126,7 +127,7 @@ class TestMMAApprovalIndicators: mock_imscope.style_color.return_value.__enter__.return_value = None mock_imscope.style_var.return_value.__enter__.return_value = None - App._render_mma_dashboard(app) + gui_2.render_mma_dashboard(app) combined = _collect_text_colored_args(imgui_mock) assert "APPROVAL PENDING" not in combined, ( "text_colored called with 'APPROVAL PENDING' when no approval is pending" @@ -147,7 +148,7 @@ class TestMMAApprovalIndicators: mock_imscope.style_var.return_value.__enter__.return_value = None mock_math.sin.return_value = 0.8 - App._render_mma_dashboard(app) + gui_2.render_mma_dashboard(app) combined = _collect_text_colored_args(imgui_mock) assert "APPROVAL PENDING" in combined, ( "text_colored not called with 'APPROVAL PENDING' when _pending_mma_spawn is set" @@ -168,7 +169,7 @@ class TestMMAApprovalIndicators: mock_imscope.style_var.return_value.__enter__.return_value = None mock_math.sin.return_value = 0.8 - App._render_mma_dashboard(app) + gui_2.render_mma_dashboard(app) combined = _collect_text_colored_args(imgui_mock) assert "APPROVAL PENDING" in combined, ( "text_colored not called with 'APPROVAL PENDING' when _pending_mma_approval is set" @@ -189,7 +190,7 @@ class TestMMAApprovalIndicators: mock_imscope.style_var.return_value.__enter__.return_value = None mock_math.sin.return_value = 0.8 - App._render_mma_dashboard(app) + gui_2.render_mma_dashboard(app) combined = _collect_text_colored_args(imgui_mock) assert "APPROVAL PENDING" in combined, ( "text_colored not called with 'APPROVAL PENDING' when _pending_ask_dialog is True" diff --git a/tests/test_mma_dashboard_streams.py b/tests/test_mma_dashboard_streams.py index a5a6e8e1..3a1bb6b6 100644 --- a/tests/test_mma_dashboard_streams.py +++ b/tests/test_mma_dashboard_streams.py @@ -1,6 +1,7 @@ from __future__ import annotations from unittest.mock import patch, MagicMock +from src import gui_2 from src.gui_2 import App @@ -64,7 +65,9 @@ class TestMMADashboardStreams: app = _make_app(mma_streams={"Tier 1": "hello"}) imgui_mock = _make_imgui_mock() imgui_mock.begin_child.return_value = True - with patch("src.gui_2.imgui", imgui_mock), patch("src.gui_2.imscope") as mock_imscope: + with patch("src.gui_2.imgui", imgui_mock), \ + patch("src.gui_2.imscope") as mock_imscope, \ + patch("src.gui_2.render_selectable_label") as mock_render: # Setup imscope mocks mock_imscope.window.return_value.__enter__.return_value = (True, True) mock_imscope.child.return_value.__enter__.return_value = True @@ -74,9 +77,9 @@ class TestMMADashboardStreams: mock_imscope.style_color.return_value.__enter__.return_value = None mock_imscope.style_var.return_value.__enter__.return_value = None - App._render_tier_stream_panel(app, "Tier 1", "Tier 1") + gui_2.render_tier_stream_panel(app, "Tier 1", "Tier 1") - app._render_selectable_label.assert_called_with('stream_Tier 1', 'hello', width=-1, multiline=True, height=0) + mock_render.assert_called_with(app, 'stream_Tier 1', 'hello', width=-1, multiline=True, height=0) def test_tier3_renders_worker_subheaders(self): """_render_tier_stream_panel for Tier 3 must render a sub-header for each worker stream key.""" @@ -86,7 +89,9 @@ class TestMMADashboardStreams: }) imgui_mock = _make_imgui_mock() imgui_mock.begin_child.return_value = True - with patch("src.gui_2.imgui", imgui_mock), patch("src.gui_2.imscope") as mock_imscope: + with patch("src.gui_2.imgui", imgui_mock), \ + patch("src.gui_2.imscope") as mock_imscope, \ + patch("src.gui_2.render_selectable_label"): # Setup imscope mocks mock_imscope.window.return_value.__enter__.return_value = (True, True) mock_imscope.child.return_value.__enter__.return_value = True @@ -96,7 +101,7 @@ class TestMMADashboardStreams: mock_imscope.style_color.return_value.__enter__.return_value = None mock_imscope.style_var.return_value.__enter__.return_value = None - App._render_tier_stream_panel(app, "Tier 3", None) + gui_2.render_tier_stream_panel(app, "Tier 3", None) text_args = " ".join(str(c) for c in imgui_mock.text.call_args_list) assert "T-001" in text_args, "imgui.text not called with 'T-001' worker sub-header" assert "T-002" in text_args, "imgui.text not called with 'T-002' worker sub-header" \ No newline at end of file diff --git a/tests/test_session_hub_merge.py b/tests/test_session_hub_merge.py index bbb9bb1d..42cf081a 100644 --- a/tests/test_session_hub_merge.py +++ b/tests/test_session_hub_merge.py @@ -1,42 +1,32 @@ import pytest import inspect - def test_session_hub_window_removed(): - import src.gui_2 as gui_2 - - source = inspect.getsource(gui_2.App._render_main_interface) - assert "Session Hub" not in source, "Session Hub window should be removed" - + import src.gui_2 as gui_2 + source = inspect.getsource(gui_2.render_main_interface) + assert "Session Hub" not in source, "Session Hub window should be removed" def test_discussion_hub_has_snapshot_tab(): - import src.gui_2 as gui_2 - - source = inspect.getsource(gui_2.App._render_discussion_hub) - assert "Snapshot" in source, "Discussion Hub should have Snapshot tab" - assert "_render_snapshot_tab" in source, "Discussion Hub should call _render_snapshot_tab" - + import src.gui_2 as gui_2 + source = inspect.getsource(gui_2.render_discussion_hub) + assert "Snapshot" in source, "Discussion Hub should have Snapshot tab" + assert "render_snapshot_tab" in source, "Discussion Hub should call render_snapshot_tab" def test_discussion_hub_has_context_composition_placeholder(): - import src.gui_2 as gui_2 - - source = inspect.getsource(gui_2.App._render_discussion_hub) - assert "Context Composition" in source, ( - "Discussion Hub should have Context Composition tab placeholder" - ) - + import src.gui_2 as gui_2 + source = inspect.getsource(gui_2.render_discussion_hub) + assert "Context Composition" in source, ( + "Discussion Hub should have Context Composition tab placeholder" + ) def test_discussion_hub_has_takes_tab(): - import src.gui_2 as gui_2 - - source = inspect.getsource(gui_2.App._render_discussion_hub) - assert "Takes" in source, "Discussion Hub should have Takes tab" - + import src.gui_2 as gui_2 + source = inspect.getsource(gui_2.render_discussion_hub) + assert "Takes" in source, "Discussion Hub should have Takes tab" def test_show_windows_no_session_hub(): - import src.app_controller as app_controller - - source = inspect.getsource(app_controller.AppController) - assert "Session Hub" not in source, ( - "Session Hub should be removed from show_windows" - ) + import src.app_controller as app_controller + source = inspect.getsource(app_controller.AppController) + assert "Session Hub" not in source, ( + "Session Hub should be removed from show_windows" + ) diff --git a/tests/test_shader_live_editor.py b/tests/test_shader_live_editor.py index 33930ae4..f72e81f5 100644 --- a/tests/test_shader_live_editor.py +++ b/tests/test_shader_live_editor.py @@ -1,8 +1,9 @@ import pytest from unittest.mock import patch, MagicMock +from src import gui_2 +from src.gui_2 import App def test_shader_live_editor_renders(): - from src.gui_2 import App app = App() app.show_windows["Shader Editor"] = True @@ -20,5 +21,5 @@ def test_shader_live_editor_renders(): mock_imscope.style_color.return_value.__enter__.return_value = None mock_imscope.style_var.return_value.__enter__.return_value = None - app._render_shader_live_editor() + gui_2.render_shader_live_editor(app) assert mock_imscope.window.called diff --git a/tests/test_takes_panel.py b/tests/test_takes_panel.py index dc1d1a3c..d80a024e 100644 --- a/tests/test_takes_panel.py +++ b/tests/test_takes_panel.py @@ -1,16 +1,15 @@ import pytest import inspect - +from src import gui_2 def test_takes_tab_replaces_placeholder(): - import src.gui_2 as gui_2 - - source = inspect.getsource(gui_2.App._gui_func) - assert "_render_takes_placeholder" not in source, "Placeholder should be replaced" + import src.gui_2 as gui_2 + source = inspect.getsource(gui_2.App._gui_func) + assert "_render_takes_placeholder" not in source, "Placeholder should be replaced" def test_takes_panel_has_synthesis(): - import src.gui_2 as gui_2 + import src.gui_2 as gui_2 - source = inspect.getsource(gui_2.App._render_takes_panel) - assert "synthesis" in source.lower(), "Should have synthesis functionality" + source = inspect.getsource(gui_2.render_takes_panel) + assert "synthesis" in source.lower(), "Should have synthesis functionality"