diff --git a/tests/pyportaltest/templates/__init__.py b/tests/pyportaltest/templates/__init__.py index dc8f3ac6..d74c92a7 100644 --- a/tests/pyportaltest/templates/__init__.py +++ b/tests/pyportaltest/templates/__init__.py @@ -113,7 +113,7 @@ def close(self, details: ASVType, delay: int = 0): def respond(): logger.debug(f"Session.Closed on {self.handle}: {details}") self.mock.EmitSignalDetailed( - "", "Closed", "a{sv}", [details], destination=self.sender + "", "Closed", "a{sv}", [details], details={"destination": self.sender} ) if delay > 0: diff --git a/tests/pyportaltest/templates/inputcapture.py b/tests/pyportaltest/templates/inputcapture.py index de610b21..2cd0b327 100644 --- a/tests/pyportaltest/templates/inputcapture.py +++ b/tests/pyportaltest/templates/inputcapture.py @@ -85,6 +85,9 @@ def load(mock, parameters={}): # signal mock.disabled_after = parameters.get("disabled-after", 0) + # How many ms to signal Session.Closed after Start + mock.close_after_enable = parameters.get("close-after-enable", 0) + mock.AddProperties( MAIN_IFACE, dbus.Dictionary( @@ -327,6 +330,10 @@ def send_disabled(): GLib.timeout_add(self.disabled_after, send_disabled) + if self.close_after_enable > 0: + session = self.active_sessions[session_handle] + session.close({}, self.close_after_enable) + except Exception as e: logger.critical(e) diff --git a/tests/pyportaltest/templates/remotedesktop.py b/tests/pyportaltest/templates/remotedesktop.py index ebf03400..f4189388 100644 --- a/tests/pyportaltest/templates/remotedesktop.py +++ b/tests/pyportaltest/templates/remotedesktop.py @@ -22,7 +22,7 @@ def load(mock, parameters): - logger.debug(f"loading {MAIN_IFACE} template") + logger.debug(f"loading {MAIN_IFACE} template with params {parameters}") params = MockParams.get(mock, MAIN_IFACE) params.delay = 500 @@ -30,6 +30,7 @@ def load(mock, parameters): params.response = parameters.get("response", 0) params.devices = parameters.get("devices", 0b111) params.sessions: Dict[str, Session] = {} + params.close_after_start = parameters.get("close-after-start", 0) mock.AddProperties( MAIN_IFACE, @@ -108,6 +109,10 @@ def Start(self, session_handle, parent_window, options, sender): request.respond(response, delay=params.delay) + if params.close_after_start > 0: + session = params.sessions[session_handle] + session.close({}, params.close_after_start) + return request.handle except Exception as e: logger.critical(e) diff --git a/tests/pyportaltest/test_inputcapture.py b/tests/pyportaltest/test_inputcapture.py index 372138e2..0c22727c 100644 --- a/tests/pyportaltest/test_inputcapture.py +++ b/tests/pyportaltest/test_inputcapture.py @@ -24,11 +24,13 @@ def __init__( zones: Optional[List[Xdp.InputCaptureZone]] = None, barriers: Optional[List[Xdp.InputCapturePointerBarrier]] = None, failed_barriers: Optional[List[Xdp.InputCapturePointerBarrier]] = None, + session_handle_token: Optional[str] = None, ): self.session = session self.zones = zones or [] self.barriers = barriers or [] self.failed_barriers = failed_barriers or [] + self.session_handle_token = session_handle_token class SessionCreationFailed(Exception): @@ -88,6 +90,17 @@ def create_session_done(portal, task, data): if session_error is not None: raise SessionCreationFailed(session_error) assert session is not None + assert session.get_session().get_session_type() == Xdp.SessionType.INPUT_CAPTURE + + # Extract our expected session id. This isn't available from + # XdpSession so we need to go around it. We can't easily get the + # sender id so the full path is hard. Let's just extract the token and + # pretend that's good enough. + method_calls = self.mock_interface.GetMethodCalls("CreateSession") + assert len(method_calls) >= 1 + _, args = method_calls.pop() # Assume the latest has our session + (_, options) = args + session_handle = options["session_handle_token"] zones = session.get_zones() @@ -147,6 +160,7 @@ def set_pointer_barriers_done(session, task, data): zones=zones, barriers=active_barriers, failed_barriers=failed_barriers, + session_handle_token=session_handle, ) def test_version(self): @@ -590,3 +604,56 @@ def session_disabled(session, options): self.mainloop.run() assert disabled_signal_received + + def test_close_session(self): + """ + Ensure that closing our session explicitly closes the session on DBus. + """ + setup = self.create_session_with_barriers() + session = setup.session + xdp_session = setup.session.get_session() + + was_closed = False + + def method_called(method_name, method_args, path): + nonlocal was_closed + + if method_name == "Close" and path.endswith(setup.session_handle_token): + was_closed = True + self.mainloop.quit() + + bus = self.get_dbus() + bus.add_signal_receiver( + handler_function=method_called, + signal_name="MethodCalled", + dbus_interface="org.freedesktop.DBus.Mock", + path_keyword="path", + ) + + xdp_session.close() + self.mainloop.run() + + assert was_closed is True + + def test_close_session_signal(self): + """ + Ensure that we get the GObject signal when our session is closed + externally. + """ + params = {"close-after-enable": 500} + setup = self.create_session_with_barriers(params) + session = setup.session + xdp_session = setup.session.get_session() + + session_closed_signal_received = False + + def session_closed(session): + nonlocal session_closed_signal_received + session_closed_signal_received = True + + xdp_session.connect("closed", session_closed) + + session.enable() + self.mainloop.run() + + assert session_closed_signal_received is True diff --git a/tests/pyportaltest/test_remotedesktop.py b/tests/pyportaltest/test_remotedesktop.py index 4250141b..bb36db47 100644 --- a/tests/pyportaltest/test_remotedesktop.py +++ b/tests/pyportaltest/test_remotedesktop.py @@ -490,3 +490,25 @@ def method_called(method_name, method_args, path): self.mainloop.run() assert was_closed is True + + def test_close_session_signal(self): + """ + Ensure that we get the GObject signal when our session is closed + externally. + """ + params = {"close-after-start": 500} + setup = self.create_session(params=params) + session = setup.session + + session_closed_signal_received = False + + def session_closed(session): + nonlocal session_closed_signal_received + session_closed_signal_received = True + self.mainloop.quit() + + session.connect("closed", session_closed) + + self.mainloop.run() + + assert session_closed_signal_received is True