from unittest.mock import Mock import pytest from fenrirscreenreader.core.clipboardSyncManager import ClipboardSyncManager def build_env( sync_enabled=False, sync_display="", screen_driver="vcsaDriver", keyboard_driver="evdevDriver", fenrir_text=None, ): def get_setting(section, setting): values = { ("clipboard", "sync_display"): sync_display, ("screen", "driver"): screen_driver, ("keyboard", "driver"): keyboard_driver, } return values[(section, setting)] settings_manager = Mock( get_setting_as_bool=Mock(return_value=sync_enabled), get_setting_as_float=Mock(return_value=0.5), get_setting=Mock(side_effect=get_setting), ) memory_manager = Mock( is_index_list_empty=Mock(return_value=fenrir_text is None), get_index_list_element=Mock(return_value=fenrir_text), add_value_to_first_index=Mock(), ) return { "runtime": { "SettingsManager": settings_manager, "MemoryManager": memory_manager, "DebugManager": Mock(write_debug_out=Mock()), } } @pytest.mark.unit def test_clipboard_sync_disabled_by_default(): manager = ClipboardSyncManager() manager.start = Mock() manager.initialize(build_env(sync_enabled=False)) assert manager.enabled is False manager.start.assert_not_called() @pytest.mark.unit def test_clipboard_sync_enabled_in_x_mode_with_current_display(monkeypatch): monkeypatch.setenv("DISPLAY", ":1") manager = ClipboardSyncManager() manager.start = Mock() manager.initialize( build_env( sync_enabled=True, screen_driver="ptyDriver", keyboard_driver="x11Driver", ) ) assert manager.enabled is True assert manager.display == ":1" manager.start.assert_called_once_with() @pytest.mark.unit def test_clipboard_sync_console_skips_empty_display(monkeypatch): monkeypatch.delenv("DISPLAY", raising=False) manager = ClipboardSyncManager() manager.start = Mock() manager.initialize(build_env(sync_enabled=True)) assert manager.enabled is False manager.start.assert_not_called() @pytest.mark.unit def test_clipboard_sync_console_allows_configured_display(monkeypatch): monkeypatch.delenv("DISPLAY", raising=False) manager = ClipboardSyncManager() manager.start = Mock() manager.initialize(build_env(sync_enabled=True, sync_display=":0")) assert manager.enabled is True assert manager.display == ":0" manager.start.assert_called_once_with() @pytest.mark.unit def test_mark_written_to_x_updates_observed_state(): manager = ClipboardSyncManager() manager.mark_written_to_x("manual export") assert manager.last_written_to_x == "manual export" assert manager.last_observed_fenrir == "manual export" assert manager.last_observed_x == "manual export" @pytest.mark.unit def test_fenrir_to_x_write_is_not_reimported(monkeypatch): env = build_env(fenrir_text="from fenrir") manager = ClipboardSyncManager() manager.env = env manager.display = ":1" write_text = Mock(return_value=True) read_text = Mock(side_effect=[None, "from fenrir"]) monkeypatch.setattr( "fenrirscreenreader.core.clipboardSyncManager.x_clipboard.write_text", write_text, ) monkeypatch.setattr( "fenrirscreenreader.core.clipboardSyncManager.x_clipboard.read_text", read_text, ) manager.poll_once() manager.poll_once() write_text.assert_called_once_with("from fenrir", ":1") env["runtime"]["MemoryManager"].add_value_to_first_index.assert_not_called() @pytest.mark.unit def test_conflicting_clipboards_do_not_swap_values(monkeypatch): env = build_env(fenrir_text="from fenrir") manager = ClipboardSyncManager() manager.env = env manager.display = ":1" write_text = Mock(return_value=True) read_text = Mock(return_value="from x") monkeypatch.setattr( "fenrirscreenreader.core.clipboardSyncManager.x_clipboard.write_text", write_text, ) monkeypatch.setattr( "fenrirscreenreader.core.clipboardSyncManager.x_clipboard.read_text", read_text, ) manager.poll_once() write_text.assert_called_once_with("from fenrir", ":1") env["runtime"]["MemoryManager"].add_value_to_first_index.assert_not_called() assert manager.last_written_to_x == "from fenrir" assert manager.last_observed_x == "from fenrir" assert manager.last_observed_fenrir == "from fenrir" @pytest.mark.unit def test_matching_clipboards_are_observed_without_duplicate_import(monkeypatch): env = build_env(fenrir_text="same text") manager = ClipboardSyncManager() manager.env = env manager.display = ":1" write_text = Mock(return_value=True) read_text = Mock(return_value="same text") monkeypatch.setattr( "fenrirscreenreader.core.clipboardSyncManager.x_clipboard.write_text", write_text, ) monkeypatch.setattr( "fenrirscreenreader.core.clipboardSyncManager.x_clipboard.read_text", read_text, ) manager.poll_once() write_text.assert_not_called() env["runtime"]["MemoryManager"].add_value_to_first_index.assert_not_called() assert manager.last_observed_x == "same text" assert manager.last_observed_fenrir == "same text" @pytest.mark.unit def test_x_to_fenrir_import_is_not_reexported(monkeypatch): env = build_env(fenrir_text=None) manager = ClipboardSyncManager() manager.env = env manager.display = ":1" write_text = Mock(return_value=True) read_text = Mock(return_value="from x") monkeypatch.setattr( "fenrirscreenreader.core.clipboardSyncManager.x_clipboard.write_text", write_text, ) monkeypatch.setattr( "fenrirscreenreader.core.clipboardSyncManager.x_clipboard.read_text", read_text, ) manager.poll_once() env["runtime"]["MemoryManager"].is_index_list_empty.return_value = False env["runtime"]["MemoryManager"].get_index_list_element.return_value = ( "from x" ) manager.poll_once() env["runtime"]["MemoryManager"].add_value_to_first_index.assert_called_once_with( "clipboardHistory", "from x" ) write_text.assert_not_called() @pytest.mark.unit def test_selected_imported_text_can_export_after_x_changes(monkeypatch): env = build_env(fenrir_text=None) manager = ClipboardSyncManager() manager.env = env manager.display = ":1" write_text = Mock(return_value=True) read_text = Mock(side_effect=["from x", "other x"]) monkeypatch.setattr( "fenrirscreenreader.core.clipboardSyncManager.x_clipboard.write_text", write_text, ) monkeypatch.setattr( "fenrirscreenreader.core.clipboardSyncManager.x_clipboard.read_text", read_text, ) manager.poll_once() env["runtime"]["MemoryManager"].is_index_list_empty.return_value = False env["runtime"]["MemoryManager"].get_index_list_element.return_value = ( "from x" ) manager.last_observed_fenrir = "different fenrir entry" manager.poll_once() write_text.assert_called_once_with("from x", ":1") @pytest.mark.unit @pytest.mark.parametrize("x_value", [None, "", object()]) def test_x_clipboard_non_text_values_are_ignored(monkeypatch, x_value): env = build_env(fenrir_text=None) manager = ClipboardSyncManager() manager.env = env manager.display = ":1" monkeypatch.setattr( "fenrirscreenreader.core.clipboardSyncManager.x_clipboard.read_text", Mock(return_value=x_value), ) manager.poll_once() env["runtime"]["MemoryManager"].add_value_to_first_index.assert_not_called() @pytest.mark.unit def test_x_clipboard_paste_exception_is_ignored(monkeypatch): env = build_env(fenrir_text=None) manager = ClipboardSyncManager() manager.env = env manager.display = ":1" monkeypatch.setattr( "fenrirscreenreader.core.clipboardSyncManager.x_clipboard.read_text", Mock(side_effect=RuntimeError("no text target")), ) manager.poll_once() env["runtime"]["MemoryManager"].add_value_to_first_index.assert_not_called() env["runtime"]["DebugManager"].write_debug_out.assert_called_once()