diff --git a/tests/conftest.py b/tests/conftest.py index 293ae29..028a880 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -13,6 +13,33 @@ _collector = install_gui_gc(_application) +# Windows: pytest's dead-symlink cleanup can raise PermissionError at session +# teardown (WinError 5 on `pytest-current`), which aborts CPython with +# STATUS_STACK_BUFFER_OVERRUN. Suppress OSError in those cleanup helpers. +try: + import _pytest.pathlib as _pytest_pathlib + + _orig_cleanup_dead_symlinks = _pytest_pathlib.cleanup_dead_symlinks + _orig_cleanup_numbered_dir = _pytest_pathlib.cleanup_numbered_dir + + def _safe_cleanup_dead_symlinks(root): + try: + _orig_cleanup_dead_symlinks(root) + except OSError: + pass + + def _safe_cleanup_numbered_dir(root, prefix, keep, consider_lock_dead_if_created_before): + try: + _orig_cleanup_numbered_dir(root, prefix, keep, consider_lock_dead_if_created_before) + except OSError: + pass + + _pytest_pathlib.cleanup_dead_symlinks = _safe_cleanup_dead_symlinks + _pytest_pathlib.cleanup_numbered_dir = _safe_cleanup_numbered_dir +except (ImportError, AttributeError): + pass + + @pytest.fixture(autouse=True) def collect_gui_cycles(): yield diff --git a/tests/test_store_regressions_20260928.py b/tests/test_store_regressions_20260928.py index a7fe322..fe02fe0 100644 --- a/tests/test_store_regressions_20260928.py +++ b/tests/test_store_regressions_20260928.py @@ -254,3 +254,48 @@ def test_frozen_run_button_starts_interpreter_not_explorerpro(tmp_path, monkeypa editor._run_code() assert launched == [(str(interpreter), [str(script)])] editor.close() + + +def test_right_click_on_unselected_row_selects_row(tmp_path, monkeypatch): + _app() + first = tmp_path / "first.txt" + second = tmp_path / "second.txt" + first.write_text("first", encoding="utf-8") + second.write_text("second", encoding="utf-8") + + browser = FileBrowser() + browser.resize(800, 500) + browser.show() + browser.navigate_to(str(tmp_path)) + for _ in range(100): + _app().processEvents() + if browser.proxy.rowCount(browser.table.rootIndex()) == 2: + break + time.sleep(.01) + + idx_first = browser.proxy.mapFromSource(browser.model.index(str(first))) + browser.table.selectRow(idx_first.row()) + assert [Path(p) for p in browser.get_selected_files()] == [first] + + idx_second = browser.proxy.mapFromSource(browser.model.index(str(second))) + assert not browser.table.selectionModel().isRowSelected(idx_second.row(), idx_second.parent()) + + menus = [] + + class CapturingMenu(QMenu): + def exec(self, *args): + menus.append(self) + + monkeypatch.setattr(browser_module, "QMenu", CapturingMenu) + pos = browser.table.visualRect(idx_second).center() + event = QContextMenuEvent( + QContextMenuEvent.Reason.Mouse, pos, browser.table.viewport().mapToGlobal(pos) + ) + QApplication.sendEvent(browser.table.viewport(), event) + + assert browser.table.selectionModel().isRowSelected(idx_second.row(), idx_second.parent()) + assert second in [Path(p) for p in browser.get_selected_files()] + + browser.close() + browser.deleteLater() + _app().processEvents()