init
Some checks failed
Docker. / Ubuntu (push) Has been cancelled
User-agent updater. / User-agent (push) Failing after 15s
Lock Threads / lock (push) Failing after 10s
Waiting for answer. / waiting-for-answer (push) Failing after 22s
Needs user action. / needs-user-action (push) Failing after 8s
Can't reproduce. / cant-reproduce (push) Failing after 8s
Close stale issues and PRs / stale (push) Has been cancelled

This commit is contained in:
allhaileris
2026-02-16 15:50:16 +03:00
commit afb81b8278
13816 changed files with 3689732 additions and 0 deletions

View File

@@ -0,0 +1,286 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
#
# This file is formatted with Python Black
from typing import Callable, Dict, Optional, NamedTuple
from gi.repository import GLib
import dbus
import dbusmock
import logging
def init_logger(name: str) -> logging.Logger:
"""
Common logging setup for the impl.portal templates. Use as:
>>> from tests.templates import init_logger
>>> logger = init_logger(__name__)
>>> logger.debug("foo")
"""
logging.basicConfig(
format="%(levelname).1s|%(name)s: %(message)s", level=logging.DEBUG
)
logger = logging.getLogger(f"templates.{name}")
logger.setLevel(logging.DEBUG)
return logger
logger = init_logger("utils")
class Response(NamedTuple):
response: int
results: Dict
class ImplRequest:
"""
Implementation of an ``org.freedesktop.impl.portal.Request`` object exposed
on the object path ``handle``.
The dbus method implementations need to be invoked asynchronously and the
async callbacks must be passed in ``cb_success`` and ``cb_error``.
The request either waits until it is closed by x-d-p (``wait_for_close``) or
responds to the request (``respond``).
"""
def __init__(
self,
mock: dbusmock.DBusMockObject,
busname: str,
handle: str,
logger: logging.Logger,
cb_success: Callable,
cb_error: Callable,
):
self.mock = mock
bus = mock.connection
proxy = bus.get_object(busname, handle)
self.mock_interface = dbus.Interface(proxy, dbusmock.MOCK_IFACE)
self.handle = handle
self.logger = logger
self.cb_success = cb_success
self.cb_error = cb_error
def respond(
self,
response: Callable | Response,
delay: int = 0,
done_cb: Callable | None = None,
) -> None:
def reply():
nonlocal response
res = None
logger.debug(f"Request {self.handle}: trying to reply")
if callable(response):
try:
res = response()
except Exception as e:
logger.critical(
f"Request {self.handle}: failed getting response: {e}"
)
self.cb_error(e)
self._unexport()
return
else:
res = response
assert res
logger.debug(f"Request {self.handle}: replying {res}")
self.cb_success(res.response, res.results)
self._unexport()
if done_cb:
done_cb()
self._export()
if delay > 0:
logger.debug(f"Request {self.handle}: scheduling delay of {delay}ms")
GLib.timeout_add(delay, reply)
else:
reply()
def wait_for_close(
self,
close_callback: Callable | None = None,
) -> None:
def closed():
logger.debug(f"Request {self.handle}: closed")
self.mock.EmitSignal(
"org.freedesktop.impl.portal.Mock",
"RequestClosed",
"s",
(self.handle,),
)
if close_callback:
try:
close_callback()
except Exception as e:
logger.critical(
f"Request {self.handle}: failed running close callback: {e}"
)
self.cb_error(e)
self._unexport()
return
response = Response(2, {})
self.cb_success(response.response, response.results)
self._unexport()
def cb_methodcall(name, args):
if name == "Close":
closed()
self._export()
logger.debug(f"Request {self.handle}: waiting for x-d-p to call close")
self.mock_interface.connect_to_signal("MethodCalled", cb_methodcall)
def _export(self):
# In the future we can pass a class extending
# dbusmock.mockobject.DBusMockObject as mock_class to avoid going
# through the mock MethodCalled signal
self.mock.AddObject(
path=self.handle,
interface="org.freedesktop.impl.portal.Request",
properties={},
methods=[
(
"Close",
"",
"",
"",
)
],
)
def _unexport(self):
self.mock.RemoveObject(self.handle)
def __str__(self):
return f"ImplRequest {self.handle}"
class ImplSession:
"""
Implementation of a org.freedesktop.impl.portal.Session object. Do not
instantiate this directly, instead use ``ImplSession.export()``. Typically
like this:
>>> s = ImplSession.export(mock, "org.freedesktop.impl.portal.Test", "/path/foo")
Where the test or the backend implementation relies on the Closed() method
of the ImplSession, provide a callback to be invoked.
>>> r.export(close_callback=my_callback)
Note that the latter only works if the test invokes methods
asynchronously.
.. attribute:: closed
Set to True if the Close() method on the Session was invoked
.. attribute:: handle
The session's object path
"""
def __init__(
self,
mock: dbusmock.DBusMockObject,
busname: str,
handle: str,
app_id: str,
):
self.mock = mock # the main mock object
self.handle = handle
self.app_id = app_id
self.closed = False
self._close_callback: Optional[Callable] = None
self.mock_object: Optional[dbusmock.DBusMockObject] = None
bus = mock.connection
proxy = bus.get_object(busname, handle)
mock_interface = dbus.Interface(proxy, dbusmock.MOCK_IFACE)
# Register for the Close() call on the impl.Session. If it gets
# called, use the side-channel SessionClosed signal so we can notify
# the test that the impl.Session was actually closed by the
# xdg-desktop-portal
def cb_methodcall(name, args):
if name == "Close":
self.closed = True
logger.debug(f"Session.Close() on {self.handle}")
if self._close_callback:
self._close_callback()
self.mock.EmitSignal(
"org.freedesktop.impl.portal.Mock",
"SessionClosed",
"s",
(self.handle,),
)
self._unexport()
mock_interface.connect_to_signal("MethodCalled", cb_methodcall)
def export(
self,
close_callback: Optional[Callable] = None,
) -> "ImplSession":
"""
Create the session on the bus. If ``close_callback`` is not None, that
callback will be invoked in response to the Close() method called on
this object.
"""
self.mock.AddObject(
path=self.handle,
interface="org.freedesktop.impl.portal.Session",
properties={},
methods=[
(
"Close",
"",
"",
"",
)
],
)
# This is a bit awkward. We need our session's DBusMockObject for
# EmitSignal of impl.portal.Session.Close. This is available in
# dbusmock.get_object() since our template runs as part of the server.
#
# In theory, EmitSignal should work on self.mock_interface but
# it doesn't and I can't figure out why.
self.mock_object = dbusmock.get_object(self.handle)
self._close_callback = close_callback
return self
def _unexport(self):
self.mock.RemoveObject(path=self.handle)
def close(self):
"""
Send out Closed signal and remove this session from the bus.
"""
assert self.mock_object is not None, "Session was never exported"
logger.debug(f"Signal Session.Closed on {self.handle}")
self.mock_object.EmitSignal(
interface="org.freedesktop.impl.portal.Session",
name="Closed",
signature="",
sigargs=(),
)
self.closed = True
self._unexport()
def __str__(self):
return f"ImplSession {self.handle}"

View File

@@ -0,0 +1,74 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
#
# This file is formatted with Python Black
# mypy: disable-error-code="misc"
from tests.templates import Response, init_logger, ImplRequest
import dbus.service
from dataclasses import dataclass
BUS_NAME = "org.freedesktop.impl.portal.Test"
MAIN_OBJ = "/org/freedesktop/portal/desktop"
SYSTEM_BUS = False
MAIN_IFACE = "org.freedesktop.impl.portal.Access"
logger = init_logger(__name__)
@dataclass
class AccessParameters:
delay: int
response: int
expect_close: bool
def load(mock, parameters={}):
logger.debug(f"Loading parameters: {parameters}")
assert not hasattr(mock, "access_params")
mock.access_params = AccessParameters(
delay=parameters.get("delay", 200),
response=parameters.get("response", 0),
expect_close=parameters.get("expect-close", False),
)
@dbus.service.method(
MAIN_IFACE,
in_signature="osssssa{sv}",
out_signature="ua{sv}",
async_callbacks=("cb_success", "cb_error"),
)
def AccessDialog(
self,
handle,
app_id,
parent_window,
title,
subtitle,
body,
options,
cb_success,
cb_error,
):
logger.debug(
f"AccessDialog({handle}, {app_id}, {parent_window}, {title}, {subtitle}, {body}, {options})"
)
params = self.access_params
request = ImplRequest(
self,
BUS_NAME,
handle,
logger,
cb_success,
cb_error,
)
if params.expect_close:
request.wait_for_close()
else:
request.respond(Response(params.response, {}), delay=params.delay)

View File

@@ -0,0 +1,64 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
#
# This file is formatted with Python Black
# mypy: disable-error-code="misc"
from tests.templates import Response, init_logger, ImplRequest
import dbus.service
import dbus
from dataclasses import dataclass
BUS_NAME = "org.freedesktop.impl.portal.Test"
MAIN_OBJ = "/org/freedesktop/portal/desktop"
SYSTEM_BUS = False
MAIN_IFACE = "org.freedesktop.impl.portal.Account"
logger = init_logger(__name__)
@dataclass
class AccountParameters:
delay: int
response: int
results: dict
expect_close: bool
def load(mock, parameters={}):
logger.debug(f"Loading parameters: {parameters}")
assert not hasattr(mock, "account_params")
mock.account_params = AccountParameters(
delay=parameters.get("delay", 200),
response=parameters.get("response", 0),
results=parameters.get("results", {}),
expect_close=parameters.get("expect-close", False),
)
@dbus.service.method(
MAIN_IFACE,
in_signature="ossa{sv}",
out_signature="ua{sv}",
async_callbacks=("cb_success", "cb_error"),
)
def GetUserInformation(self, handle, app_id, window, options, cb_success, cb_error):
logger.debug(f"GetUserInformation({handle}, {app_id}, {window}, {options})")
params = self.account_params
request = ImplRequest(
self,
BUS_NAME,
handle,
logger,
cb_success,
cb_error,
)
if params.expect_close:
request.wait_for_close()
else:
request.respond(Response(params.response, params.results), delay=params.delay)

View File

@@ -0,0 +1,84 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
#
# This file is formatted with Python Black
# mypy: disable-error-code="misc"
from tests.templates import Response, init_logger, ImplRequest
import dbus.service
from dataclasses import dataclass
BUS_NAME = "org.freedesktop.impl.portal.Test"
MAIN_OBJ = "/org/freedesktop/portal/desktop"
SYSTEM_BUS = False
MAIN_IFACE = "org.freedesktop.impl.portal.AppChooser"
VERSION = 2
logger = init_logger(__name__)
@dataclass
class AppchooserParameters:
delay: int
response: int
expect_close: bool
def load(mock, parameters={}):
logger.debug(f"Loading parameters: {parameters}")
assert not hasattr(mock, "appchooser_params")
mock.appchooser_params = AppchooserParameters(
delay=parameters.get("delay", 200),
response=parameters.get("response", 0),
expect_close=parameters.get("expect-close", False),
)
mock.AddProperties(
MAIN_IFACE,
dbus.Dictionary(
{
"version": dbus.UInt32(parameters.get("version", VERSION)),
}
),
)
@dbus.service.method(
MAIN_IFACE,
in_signature="ossasa{sv}",
out_signature="ua{sv}",
async_callbacks=("cb_success", "cb_error"),
)
def ChooseApplication(
self, handle, app_id, parent_window, choices, options, cb_success, cb_error
):
logger.debug(
f"ChooseApplication({handle}, {app_id}, {parent_window}, {choices}, {options})"
)
params = self.appchooser_params
request = ImplRequest(
self,
BUS_NAME,
handle,
logger,
cb_success,
cb_error,
)
if params.expect_close:
request.wait_for_close()
else:
request.respond(Response(params.response, {}), delay=params.delay)
@dbus.service.method(
MAIN_IFACE,
in_signature="oas",
out_signature="",
)
def UpdateChoices(self, handle, choices):
logger.debug(f"UpdateChoices({handle}, {choices})")

View File

@@ -0,0 +1,76 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
#
# This file is formatted with Python Black
# mypy: disable-error-code="misc"
from tests.templates import init_logger
import dbus.service
import dbus
from gi.repository import GLib
from dataclasses import dataclass
BUS_NAME = "org.freedesktop.impl.portal.Test"
MAIN_OBJ = "/org/freedesktop/portal/desktop"
SYSTEM_BUS = False
MAIN_IFACE = "org.freedesktop.impl.portal.Background"
VERSION = 1
logger = init_logger(__name__)
@dataclass
class BackgroundParameters:
delay: int
def load(mock, parameters={}):
logger.debug(f"Loading parameters: {parameters}")
assert not hasattr(mock, "background_params")
mock.background_params = BackgroundParameters(
delay=parameters.get("delay", 200),
)
@dbus.service.method(
MAIN_IFACE,
in_signature="",
out_signature="a{sv}",
async_callbacks=("cb_success", "cb_error"),
)
def GetAppState(self, cb_success, cb_error):
logger.debug("GetAppState()")
params = self.background_params
# FIXME: implement?
def reply():
cb_success({})
logger.debug(f"scheduling delay of {params.delay}")
GLib.timeout_add(params.delay, reply)
@dbus.service.method(
MAIN_IFACE,
in_signature="oss",
out_signature="ua{sv}",
async_callbacks=("cb_success", "cb_error"),
)
def NotifyBackground(self, handle, app_id, name, cb_success, cb_error):
logger.debug(f"NotifyBackground({handle}, {app_id}, {name})")
params = self.background_params
logger.debug(f"scheduling delay of {params.delay}")
GLib.timeout_add(params.delay, cb_success)
@dbus.service.method(
MAIN_IFACE,
in_signature="sbasu",
out_signature="b",
)
def EnableAutostart(self, app_id, enable, commandline, flags):
raise dbus.exceptions.DBusException("EnableAutostart is deprecated")

View File

@@ -0,0 +1,168 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
#
# This file is formatted with Python Black
# mypy: disable-error-code="misc"
from tests.templates import init_logger
import dbus.service
import dbus
import tempfile
from gi.repository import GLib
from dataclasses import dataclass
BUS_NAME = "org.freedesktop.impl.portal.Test"
MAIN_OBJ = "/org/freedesktop/portal/desktop"
SYSTEM_BUS = False
MAIN_IFACE = "org.freedesktop.impl.portal.Clipboard"
VERSION = 1
logger = init_logger(__name__)
@dataclass
class ClipboardParameters:
delay: int
response: int
expect_close: bool
def load(mock, parameters={}):
logger.debug(f"Loading parameters: {parameters}")
assert not hasattr(mock, "clipboard_params")
mock.clipboard_params = ClipboardParameters(
delay=parameters.get("delay", 200),
response=parameters.get("response", 0),
expect_close=parameters.get("expect-close", False),
)
mock.AddProperties(
MAIN_IFACE,
dbus.Dictionary(
{
"version": dbus.UInt32(parameters.get("version", VERSION)),
}
),
)
@dbus.service.method(
MAIN_IFACE,
in_signature="oa{sv}",
out_signature="",
async_callbacks=("cb_success", "cb_error"),
)
def RequestClipboard(self, session_handle, options, cb_success, cb_error):
try:
logger.debug(f"RequestClipboard({session_handle}, {options})")
params = self.clipboard_params
if params.expect_close:
cb_success()
else:
logger.debug(f"scheduling delay of {params.delay}")
GLib.timeout_add(params.delay, cb_success)
except Exception as e:
logger.critical(e)
cb_error(e)
@dbus.service.method(
MAIN_IFACE,
in_signature="oa{sv}",
out_signature="",
async_callbacks=("cb_success", "cb_error"),
)
def SetSelection(self, session_handle, options, cb_success, cb_error):
try:
logger.debug(f"SetSelection({session_handle}, {options})")
params = self.clipboard_params
if params.expect_close:
cb_success()
else:
logger.debug(f"scheduling delay of {params.delay}")
GLib.timeout_add(params.delay, cb_success)
except Exception as e:
logger.critical(e)
cb_error(e)
@dbus.service.method(
MAIN_IFACE,
in_signature="ou",
out_signature="h",
async_callbacks=("cb_success", "cb_error"),
)
def SelectionWrite(self, session_handle, serial, cb_success, cb_error):
try:
logger.debug(f"SelectionWrite({session_handle}, {serial})")
params = self.clipboard_params
temp_file = tempfile.TemporaryFile()
fd = dbus.types.UnixFd(temp_file.fileno())
if params.expect_close:
cb_success(fd)
else:
def reply():
cb_success(fd)
logger.debug(f"scheduling delay of {params.delay}")
GLib.timeout_add(params.delay, reply)
except Exception as e:
logger.critical(e)
cb_error(e)
@dbus.service.method(
MAIN_IFACE,
in_signature="oub",
out_signature="",
async_callbacks=("cb_success", "cb_error"),
)
def SelectionWriteDone(self, session_handle, serial, success, cb_success, cb_error):
try:
logger.debug(f"SelectionWriteDone({session_handle}, {serial}, {success})")
params = self.clipboard_params
if params.expect_close:
cb_success()
else:
logger.debug(f"scheduling delay of {params.delay}")
GLib.timeout_add(params.delay, cb_success)
except Exception as e:
logger.critical(e)
cb_error(e)
@dbus.service.method(
MAIN_IFACE,
in_signature="os",
out_signature="h",
async_callbacks=("cb_success", "cb_error"),
)
def SelectionRead(self, session_handle, mime_type, cb_success, cb_error):
try:
logger.debug(f"SelectionRead({session_handle}, {mime_type})")
params = self.clipboard_params
temp_file = tempfile.TemporaryFile()
fd = dbus.types.UnixFd(temp_file.fileno())
if params.expect_close:
cb_success(fd)
else:
def reply():
cb_success(fd)
logger.debug(f"scheduling delay of {params.delay}")
GLib.timeout_add(params.delay, reply)
except Exception as e:
logger.critical(e)
cb_error(e)

View File

@@ -0,0 +1,85 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
#
# This file is formatted with Python Black
# mypy: disable-error-code="misc"
from tests.templates import Response, init_logger, ImplRequest
import dbus.service
from dataclasses import dataclass
BUS_NAME = "org.freedesktop.impl.portal.Test"
MAIN_OBJ = "/org/freedesktop/portal/desktop"
SYSTEM_BUS = False
MAIN_IFACE = "org.freedesktop.impl.portal.DynamicLauncher"
VERSION = 1
logger = init_logger(__name__)
@dataclass
class DynamiclauncherParameters:
delay: int
response: int
expect_close: bool
launcher_name: str
def load(mock, parameters={}):
logger.debug(f"Loading parameters: {parameters}")
assert not hasattr(mock, "dynamiclauncher_params")
mock.dynamiclauncher_params = DynamiclauncherParameters(
delay=parameters.get("delay", 200),
response=parameters.get("response", 0),
expect_close=parameters.get("expect-close", False),
launcher_name=parameters.get("launcher-name", None),
)
mock.AddProperties(
MAIN_IFACE,
dbus.Dictionary(
{
"version": dbus.UInt32(parameters.get("version", VERSION)),
}
),
)
@dbus.service.method(
MAIN_IFACE,
in_signature="osssva{sv}",
out_signature="ua{sv}",
async_callbacks=("cb_success", "cb_error"),
)
def PrepareInstall(
self, handle, app_id, parent_window, name, icon_v, options, cb_success, cb_error
):
logger.debug(
f"PrepareInstall({handle}, {app_id}, {parent_window}, {name}, {icon_v}, {options})"
)
params = self.dynamiclauncher_params
request = ImplRequest(
self,
BUS_NAME,
handle,
logger,
cb_success,
cb_error,
)
response = Response(
params.response,
{
"name": params.launcher_name if params.launcher_name else name,
"icon": dbus.Struct(list(icon_v), signature="sv", variant_level=2),
},
)
if params.expect_close:
request.wait_for_close()
else:
request.respond(response, delay=params.delay)

View File

@@ -0,0 +1,71 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
#
# This file is formatted with Python Black
# mypy: disable-error-code="misc"
from tests.templates import Response, init_logger, ImplRequest
import dbus.service
from dataclasses import dataclass
BUS_NAME = "org.freedesktop.impl.portal.Test"
MAIN_OBJ = "/org/freedesktop/portal/desktop"
SYSTEM_BUS = False
MAIN_IFACE = "org.freedesktop.impl.portal.Email"
VERSION = 3
logger = init_logger(__name__)
@dataclass
class EmailParameters:
delay: int
response: int
expect_close: bool
def load(mock, parameters={}):
logger.debug(f"Loading parameters: {parameters}")
assert not hasattr(mock, "email_params")
mock.email_params = EmailParameters(
delay=parameters.get("delay", 200),
response=parameters.get("response", 0),
expect_close=parameters.get("expect-close", False),
)
mock.AddProperties(
MAIN_IFACE,
dbus.Dictionary(
{
"version": dbus.UInt32(parameters.get("version", VERSION)),
}
),
)
@dbus.service.method(
MAIN_IFACE,
in_signature="ossa{sv}",
out_signature="ua{sv}",
async_callbacks=("cb_success", "cb_error"),
)
def ComposeEmail(self, handle, app_id, parent_window, options, cb_success, cb_error):
logger.debug(f"ComposeEmail({handle}, {app_id}, {parent_window}, {options})")
params = self.email_params
request = ImplRequest(
self,
BUS_NAME,
handle,
logger,
cb_success,
cb_error,
)
if params.expect_close:
request.wait_for_close()
else:
request.respond(Response(params.response, {}), delay=params.delay)

View File

@@ -0,0 +1,98 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
#
# This file is formatted with Python Black
# mypy: disable-error-code="misc"
from tests.templates import Response, init_logger, ImplRequest
import dbus.service
from dataclasses import dataclass
BUS_NAME = "org.freedesktop.impl.portal.Test"
MAIN_OBJ = "/org/freedesktop/portal/desktop"
SYSTEM_BUS = False
MAIN_IFACE = "org.freedesktop.impl.portal.FileChooser"
VERSION = 4
logger = init_logger(__name__)
@dataclass
class FilechooserParameters:
delay: int
response: int
results: dict
expect_close: bool
def load(mock, parameters={}):
logger.debug(f"Loading parameters: {parameters}")
assert not hasattr(mock, "filechooser_params")
mock.filechooser_params = FilechooserParameters(
delay=parameters.get("delay", 200),
response=parameters.get("response", 0),
results=parameters.get("results", {}),
expect_close=parameters.get("expect-close", False),
)
mock.AddProperties(
MAIN_IFACE,
dbus.Dictionary(
{
"version": dbus.UInt32(parameters.get("version", VERSION)),
}
),
)
@dbus.service.method(
MAIN_IFACE,
in_signature="osssa{sv}",
out_signature="ua{sv}",
async_callbacks=("cb_success", "cb_error"),
)
def OpenFile(self, handle, app_id, parent_window, title, options, cb_success, cb_error):
logger.debug(f"OpenFile({handle}, {app_id}, {parent_window}, {title}, {options})")
params = self.filechooser_params
request = ImplRequest(
self,
BUS_NAME,
handle,
logger,
cb_success,
cb_error,
)
if params.expect_close:
request.wait_for_close()
else:
request.respond(Response(params.response, params.results), delay=params.delay)
@dbus.service.method(
MAIN_IFACE,
in_signature="osssa{sv}",
out_signature="ua{sv}",
async_callbacks=("cb_success", "cb_error"),
)
def SaveFile(self, handle, app_id, parent_window, title, options, cb_success, cb_error):
logger.debug(f"SaveFile({handle}, {app_id}, {parent_window}, {title}, {options})")
params = self.filechooser_params
request = ImplRequest(
self,
BUS_NAME,
handle,
logger,
cb_success,
cb_error,
)
if params.expect_close:
request.wait_for_close()
else:
request.respond(Response(params.response, params.results), delay=params.delay)

View File

@@ -0,0 +1,121 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
# mypy: disable-error-code="misc"
from tests.templates import init_logger
import dbus.service
import dbus
from dbusmock import mockobject
BUS_NAME = "org.freedesktop.GeoClue2"
MAIN_OBJ = "/org/freedesktop/GeoClue2/Manager"
MAIN_IFACE = "org.freedesktop.GeoClue2.Manager"
CLIENT_IFACE = "org.freedesktop.GeoClue2.Client"
LOCATION_IFACE = "org.freedesktop.GeoClue2.Location"
MOCK_IFACE = "org.freedesktop.GeoClue2.Mock"
SYSTEM_BUS = True
VERSION = 1
logger = init_logger(__name__)
def load(mock, parameters={}):
mock.AddMethods(
MAIN_IFACE,
[
(
"GetClient",
"",
"o",
'ret = dbus.ObjectPath("/org/freedesktop/GeoClue2/Client/1")',
),
],
)
mock.AddObject(
"/org/freedesktop/GeoClue2/Client/1",
CLIENT_IFACE,
{
"DesktopId": "",
"DistanceThreshold": 0,
"TimeThreshold": 0,
"RequestedAccuracyLevel": 0,
},
[
("Start", "", "", Start),
("Stop", "", "", Stop),
],
)
mock.client = mockobject.objects["/org/freedesktop/GeoClue2/Client/1"]
mock.client.manager = mock
mock.client.started = False
mock.client.location = 0
mock.client.props = {"Latitude": 0, "Longitude": 0, "Accuracy": 0}
mock.client.AddMethod(MOCK_IFACE, "ChangeLocation", "a{sv}", "", ChangeLocation)
@dbus.service.method(
CLIENT_IFACE,
in_signature="",
out_signature="",
)
def Start(self):
logger.debug("Start()")
self.started = True
self.ChangeLocation(self.props)
@dbus.service.method(
CLIENT_IFACE,
in_signature="",
out_signature="",
)
def Stop(self):
logger.debug("Stop()")
self.started = False
self.RemoveObject(f"/org/freedesktop/GeoClue2/Location/{self.location}")
@dbus.service.method(
MOCK_IFACE,
in_signature="a{sv}",
out_signature="",
)
def ChangeLocation(self, props):
logger.debug(f"ChangeLocation({props})")
self.props = props
if not self.started:
return
old_path = "/"
if self.location > 0:
old_path = f"/org/freedesktop/GeoClue2/Location/{self.location}"
self.location = self.location + 1
new_path = f"/org/freedesktop/GeoClue2/Location/{self.location}"
self.AddObject(
new_path,
LOCATION_IFACE,
{
"Latitude": props["Latitude"],
"Longitude": props["Longitude"],
"Accuracy": props["Accuracy"],
},
[],
)
if old_path != "/":
self.RemoveObject(old_path)
self.EmitSignal(
CLIENT_IFACE,
"LocationUpdated",
"oo",
[
dbus.ObjectPath(old_path),
dbus.ObjectPath(new_path),
],
)

View File

@@ -0,0 +1,165 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
#
# This file is formatted with Python Black
# mypy: disable-error-code="misc"
from tests.templates import Response, init_logger, ImplRequest, ImplSession
import dbus
import dbus.service
import time
from dbusmock import MOCK_IFACE
from gi.repository import GLib
from dataclasses import dataclass
BUS_NAME = "org.freedesktop.impl.portal.Test"
MAIN_OBJ = "/org/freedesktop/portal/desktop"
SYSTEM_BUS = False
MAIN_IFACE = "org.freedesktop.impl.portal.GlobalShortcuts"
VERSION = 1
logger = init_logger(__name__)
@dataclass
class GlobalshortcutsParameters:
delay: int
response: int
expect_close: bool
force_close: int
def load(mock, parameters={}):
logger.debug(f"Loading parameters: {parameters}")
assert not hasattr(mock, "globalshortcuts_params")
mock.globalshortcuts_params = GlobalshortcutsParameters(
delay=parameters.get("delay", 200),
response=parameters.get("response", 0),
expect_close=parameters.get("expect-close", False),
force_close=parameters.get("force-close", 0),
)
mock.AddProperties(
MAIN_IFACE,
dbus.Dictionary(
{
"version": dbus.UInt32(parameters.get("version", VERSION)),
}
),
)
mock.sessions: dict[str, ImplSession] = {}
@dbus.service.method(
MAIN_IFACE,
in_signature="oosa{sv}",
out_signature="ua{sv}",
async_callbacks=("cb_success", "cb_error"),
)
def CreateSession(self, handle, session_handle, app_id, options, cb_success, cb_error):
logger.debug(f"CreateSession({handle}, {session_handle}, {app_id}, {options})")
params = self.globalshortcuts_params
session = ImplSession(self, BUS_NAME, session_handle, app_id).export()
self.sessions[session_handle] = session
request = ImplRequest(
self,
BUS_NAME,
handle,
logger,
cb_success,
cb_error,
)
if params.expect_close:
request.wait_for_close()
else:
request.respond(
Response(params.response, {"session_handle": session.handle}),
delay=params.delay,
)
if params.force_close > 0:
GLib.timeout_add(params.force_close, session.close)
@dbus.service.method(
MAIN_IFACE,
in_signature="ooa(sa{sv})sa{sv}",
out_signature="ua{sv}",
async_callbacks=("cb_success", "cb_error"),
)
def BindShortcuts(
self,
handle,
session_handle,
shortcuts,
parent_window,
options,
cb_success,
cb_error,
):
logger.debug(f"BindShortcuts({handle}, {session_handle}, {shortcuts}, {options})")
params = self.globalshortcuts_params
assert session_handle in self.sessions
request = ImplRequest(
self,
BUS_NAME,
handle,
logger,
cb_success,
cb_error,
)
if params.expect_close:
request.wait_for_close()
else:
def reply():
logger.debug(f"BindShortcuts with shortcuts {shortcuts}")
self.sessions[session_handle].shortcuts = shortcuts
return Response(params.response, {})
request.respond(reply, delay=params.delay)
@dbus.service.method(
MAIN_IFACE,
in_signature="oo",
out_signature="ua{sv}",
)
def ListShortcuts(
self,
handle,
session_handle,
):
shortcuts = self.sessions[session_handle].shortcuts
return (0, {"shortcuts": shortcuts})
@dbus.service.method(
MOCK_IFACE,
in_signature="os",
out_signature="",
)
def Trigger(self, session_handle, shortcut_id):
now_since_epoch = int(time.time() * 1000000)
self.EmitSignal(
MAIN_IFACE,
"Activated",
"osta{sv}",
[session_handle, shortcut_id, now_since_epoch, {}],
)
time.sleep(0.2)
now_since_epoch = int(time.time() * 1000000)
self.EmitSignal(
MAIN_IFACE,
"Deactivated",
"osta{sv}",
[session_handle, shortcut_id, now_since_epoch, {}],
)

View File

@@ -0,0 +1,164 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
#
# This file is formatted with Python Black
# mypy: disable-error-code="misc"
from tests.templates import Response, init_logger, ImplRequest, ImplSession
import dbus.service
from gi.repository import GLib
from enum import Enum
from dbusmock import MOCK_IFACE
from dataclasses import dataclass
BUS_NAME = "org.freedesktop.impl.portal.Test"
MAIN_OBJ = "/org/freedesktop/portal/desktop"
SYSTEM_BUS = False
MAIN_IFACE = "org.freedesktop.impl.portal.Inhibit"
VERSION = 3
logger = init_logger(__name__)
class SessionState(Enum):
RUNNING = 1
QUERY_END = 2
ENDING = 3
@dataclass
class InhibitParameters:
delay: int
response: int
expect_close: bool
def load(mock, parameters={}):
logger.debug(f"Loading parameters: {parameters}")
assert not hasattr(mock, "inhibit_params")
mock.inhibit_params = InhibitParameters(
delay=parameters.get("delay", 200),
response=parameters.get("response", 0),
expect_close=parameters.get("expect-close", False),
)
mock.AddProperties(
MAIN_IFACE,
dbus.Dictionary(
{
"version": dbus.UInt32(parameters.get("version", VERSION)),
}
),
)
mock.sessions: dict[str, ImplSession] = {}
mock.session_timers = {}
@dbus.service.method(
MAIN_IFACE,
in_signature="ossua{sv}",
out_signature="",
async_callbacks=("cb_success", "cb_error"),
)
def Inhibit(self, handle, app_id, window, flags, options, cb_success, cb_error):
logger.debug(f"Inhibit({handle}, {app_id}, {window}, {flags}, {options})")
params = self.inhibit_params
request = ImplRequest(
self,
BUS_NAME,
handle,
logger,
cb_success,
cb_error,
)
if params.expect_close:
request.wait_for_close()
else:
request.respond(Response(params.response, {}), delay=params.delay)
@dbus.service.method(
MOCK_IFACE,
in_signature="s",
out_signature="",
)
def ArmTimer(self, session_handle):
self.EmitSignal(
MAIN_IFACE,
"StateChanged",
"oa{sv}",
[
session_handle,
{
"screensaver-active": False,
"session-state": SessionState.QUERY_END.value,
},
],
)
def close_session():
session = self.sessions[session_handle]
session.close()
self.sessions[session_handle] = None
if session_handle in self.session_timers:
GLib.source_remove(self.session_timers[session_handle])
self.session_timers[session_handle] = GLib.timeout_add(700, close_session)
@dbus.service.method(
MAIN_IFACE,
in_signature="ooss",
out_signature="u",
async_callbacks=("cb_success", "cb_error"),
)
def CreateMonitor(self, handle, session_handle, app_id, window, cb_success, cb_error):
logger.debug(f"CreateMonitor({handle}, {session_handle}, {app_id}, {window})")
params = self.inhibit_params
session = ImplSession(self, BUS_NAME, session_handle, app_id).export()
self.sessions[session_handle] = session
# This is irregular: the backend doesn't return the results vardict
def internal_cb_success(response, results):
cb_success(response)
request = ImplRequest(
self,
BUS_NAME,
handle,
logger,
internal_cb_success,
cb_error,
)
if params.expect_close:
request.wait_for_close()
else:
def arm_timer():
self.ArmTimer(session_handle)
request.respond(
Response(params.response, {}), delay=params.delay, done_cb=arm_timer
)
@dbus.service.method(
MAIN_IFACE,
in_signature="o",
out_signature="",
)
def QueryEndResponse(self, session_handle):
try:
logger.debug(f"QueryEndResponse({session_handle})")
self.ArmTimer(session_handle)
except Exception as e:
logger.critical(e)

View File

@@ -0,0 +1,336 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
#
# This file is formatted with Python Black
# mypy: disable-error-code="misc"
from tests.templates import Response, init_logger
from collections import namedtuple
from itertools import count
from gi.repository import GLib
from dataclasses import dataclass
import dbus
import dbus.service
import socket
BUS_NAME = "org.freedesktop.impl.portal.Test"
MAIN_OBJ = "/org/freedesktop/portal/desktop"
SYSTEM_BUS = False
MAIN_IFACE = "org.freedesktop.impl.portal.InputCapture"
VERSION = 1
logger = init_logger(__name__)
serials = count()
Barrier = namedtuple("Barrier", ["id", "position"])
@dataclass
class InputcaptureParameters:
delay: int
supported_capabilities: int
capabilities: int
default_zone: list
disable_delay: int
activated_delay: int
deactivated_delay: int
zones_changed_delay: int
def load(mock, parameters={}):
logger.debug(f"Loading parameters: {parameters}")
assert not hasattr(mock, "inputcapture_params")
mock.inputcapture_params = InputcaptureParameters(
delay=parameters.get("delay", 0),
supported_capabilities=parameters.get("supported_capabilities", 0xF),
capabilities=parameters.get("capabilities", None),
default_zone=parameters.get("default-zone", [(1920, 1080, 0, 0)]),
disable_delay=parameters.get("disable-delay", 0),
activated_delay=parameters.get("activated-delay", 0),
deactivated_delay=parameters.get("deactivated-delay", 0),
zones_changed_delay=parameters.get("zones-changed-delay", 0),
)
mock.current_zones = mock.inputcapture_params.default_zone
mock.current_zone_set = next(serials)
mock.AddProperties(
MAIN_IFACE,
dbus.Dictionary(
{
"version": dbus.UInt32(parameters.get("version", VERSION)),
"SupportedCapabilities": dbus.UInt32(
mock.inputcapture_params.supported_capabilities
),
}
),
)
mock.active_session_handles = []
@dbus.service.method(
MAIN_IFACE,
in_signature="oossa{sv}",
out_signature="ua{sv}",
)
def CreateSession(self, handle, session_handle, app_id, parent_window, options):
try:
logger.debug(f"CreateSession({parent_window}, {options})")
params = self.inputcapture_params
assert "capabilities" in options
# Filter to the subset of supported capabilities
if params.capabilities is None:
capabilities = options["capabilities"]
else:
capabilities = params.capabilities
capabilities &= params.supported_capabilities
response = Response(0, {})
response.results["capabilities"] = dbus.UInt32(capabilities)
self.active_session_handles.append(session_handle)
logger.debug(f"CreateSession with response {response}")
return response.response, response.results
except Exception as e:
logger.critical(e)
return (2, {})
@dbus.service.method(
MAIN_IFACE,
in_signature="oosa{sv}",
out_signature="ua{sv}",
)
def GetZones(self, handle, session_handle, app_id, options):
try:
logger.debug(f"GetZones({session_handle}, {options})")
params = self.inputcapture_params
assert session_handle in self.active_session_handles
response = Response(0, {})
response.results["zones"] = params.default_zone
response.results["zone_set"] = dbus.UInt32(
self.current_zone_set, variant_level=1
)
logger.debug(f"GetZones with response {response}")
if response.response == 0:
self.current_zones = response.results["zones"]
return response.response, response.results
except Exception as e:
logger.critical(e)
return (2, {})
@dbus.service.method(
MAIN_IFACE,
in_signature="oosa{sv}aa{sv}u",
out_signature="ua{sv}",
)
def SetPointerBarriers(
self, handle, session_handle, app_id, options, barriers, zone_set
):
try:
logger.debug(
f"SetPointerBarriers({session_handle}, {options}, {barriers}, {zone_set})"
)
assert session_handle in self.active_session_handles
assert zone_set == self.current_zone_set
self.current_barriers = []
failed_barriers = []
# Barrier sanity checks:
for b in barriers:
id = b["barrier_id"]
x1, y1, x2, y2 = b["position"]
if (x1 != x2 and y1 != y2) or (x1 == x2 and y1 == y2):
logger.debug(f"Barrier {id} is not horizontal or vertical")
failed_barriers.append(id)
continue
for z in self.current_zones:
w, h, x, y = z
if x1 < x or x1 > x + w:
continue
if y1 < y or y1 > y + h:
continue
# x1/y1 fit into our current zone
if x2 < x or x2 > x + w or y2 < y or y2 > y + h:
logger.debug(f"Barrier {id} spans multiple zones")
elif x1 == x2 and (x1 != x and x1 != x + w):
logger.debug(f"Barrier {id} is not on vertical edge")
elif y1 == y2 and (y1 != y and y1 != y + h):
logger.debug(f"Barrier {id} is not on horizontal edge")
else:
self.current_barriers.append(Barrier(id=id, position=b["position"]))
break
failed_barriers.append(id)
break
else:
logger.debug(f"Barrier {id} does not fit into any zone")
failed_barriers.append(id)
continue
response = Response(0, {})
response.results["failed_barriers"] = dbus.Array(
[dbus.UInt32(f) for f in failed_barriers],
signature="u",
variant_level=1,
)
logger.debug(f"SetPointerBarriers with response {response}")
return response.response, response.results
except Exception as e:
logger.critical(e)
return (2, {})
@dbus.service.method(
MAIN_IFACE,
in_signature="osa{sv}",
out_signature="ua{sv}",
)
def Enable(self, session_handle, app_id, options):
try:
logger.debug(f"Enable({session_handle}, {options})")
params = self.inputcapture_params
assert session_handle in self.active_session_handles
# for use in the signals
activation_id = next(serials)
barrier = self.current_barriers[0]
pos = (barrier.position[0] + 10, barrier.position[1] + 20)
if params.disable_delay > 0:
def disable():
logger.debug("emitting Disabled")
self.EmitSignal(MAIN_IFACE, "Disabled", "oa{sv}", [session_handle, {}])
GLib.timeout_add(params.disable_delay, disable)
if params.activated_delay > 0:
def activated():
logger.debug("emitting Activated")
options = {
"activation_id": dbus.UInt32(activation_id, variant_level=1),
"barrier_id": dbus.UInt32(barrier.id, variant_level=1),
"cursor_position": dbus.Struct(
pos, signature="dd", variant_level=1
),
}
self.EmitSignal(
MAIN_IFACE, "Activated", "oa{sv}", [session_handle, options]
)
GLib.timeout_add(params.activated_delay, activated)
if params.deactivated_delay > 0:
def deactivated():
logger.debug("emitting Deactivated")
options = {
"activation_id": dbus.UInt32(activation_id, variant_level=1),
"cursor_position": dbus.Struct(
pos, signature="dd", variant_level=1
),
}
self.EmitSignal(
MAIN_IFACE, "Deactivated", "oa{sv}", [session_handle, options]
)
GLib.timeout_add(params.deactivated_delay, deactivated)
if params.zones_changed_delay > 0:
def zones_changed():
logger.debug("emitting ZonesChanged")
options = {
"zone_set": dbus.UInt32(activation_id, variant_level=1),
}
self.EmitSignal(
MAIN_IFACE, "ZonesChanged", "oa{sv}", [session_handle, options]
)
GLib.timeout_add(params.zones_changed_delay, zones_changed)
except Exception as e:
logger.critical(e)
return (2, {})
@dbus.service.method(
MAIN_IFACE,
in_signature="osa{sv}",
out_signature="ua{sv}",
)
def Disable(self, session_handle, app_id, options):
try:
logger.debug(f"Disable({session_handle}, {options})")
assert session_handle in self.active_session_handles
except Exception as e:
logger.critical(e)
return (2, {})
@dbus.service.method(
MAIN_IFACE,
in_signature="osa{sv}",
out_signature="ua{sv}",
)
def Release(self, session_handle, app_id, options):
try:
logger.debug(f"Release({session_handle}, {options})")
assert session_handle in self.active_session_handles
except Exception as e:
logger.critical(e)
return (2, {})
@dbus.service.method(
MAIN_IFACE,
in_signature="osa{sv}",
out_signature="h",
)
def ConnectToEIS(self, session_handle, app_id, options):
try:
logger.debug(f"ConnectToEIS({session_handle}, {options})")
assert session_handle in self.active_session_handles
sockets = socket.socketpair()
self.eis_socket = sockets[0]
assert self.eis_socket.send(b"HELLO") == 5
fd = sockets[1]
logger.debug(f"ConnectToEis with fd {fd.fileno()}")
return dbus.types.UnixFd(fd)
except Exception as e:
logger.critical(e)
return -1

View File

@@ -0,0 +1,48 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
#
# This file is formatted with Python Black
# mypy: disable-error-code="misc"
from tests.templates import init_logger
import dbus.service
BUS_NAME = "org.freedesktop.impl.portal.Test"
MAIN_OBJ = "/org/freedesktop/portal/desktop"
SYSTEM_BUS = False
MAIN_IFACE = "org.freedesktop.impl.portal.Lockdown"
logger = init_logger(__name__)
def load(mock, parameters={}):
logger.debug(f"Loading parameters: {parameters}")
mock.AddProperties(
MAIN_IFACE,
dbus.Dictionary(
{
"disable-printing": dbus.Boolean(
parameters.get("disable-printing", False)
),
"disable-save-to-disk": dbus.Boolean(
parameters.get("disable-save-to-disk", False)
),
"disable-application-handlers": dbus.Boolean(
parameters.get("disable-application-handlers", False)
),
"disable-location": dbus.Boolean(
parameters.get("disable-location", False)
),
"disable-camera": dbus.Boolean(parameters.get("disable-camera", False)),
"disable-microphone": dbus.Boolean(
parameters.get("disable-microphone", False)
),
"disable-sound-output": dbus.Boolean(
parameters.get("disable-sound-output", False)
),
}
),
)

View File

@@ -0,0 +1,82 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
#
# This file is formatted with Python Black
# mypy: disable-error-code="misc"
from tests.templates import init_logger
import dbus.service
from dbusmock import MOCK_IFACE
BUS_NAME = "org.freedesktop.impl.portal.Test"
MAIN_OBJ = "/org/freedesktop/portal/desktop"
SYSTEM_BUS = False
MAIN_IFACE = "org.freedesktop.impl.portal.Notification"
VERSION = 2
logger = init_logger(__name__)
def load(mock, parameters={}):
logger.debug(f"Loading parameters: {parameters}")
mock.AddProperties(
MAIN_IFACE,
dbus.Dictionary(
{
"version": dbus.UInt32(parameters.get("version", VERSION)),
"SupportedOptions": dbus.Dictionary(
parameters.get("SupportedOptions", {}), signature="sv"
),
},
),
)
mock.notifications = {}
@dbus.service.method(
MAIN_IFACE,
in_signature="ssa{sv}",
out_signature="",
)
def AddNotification(self, app_id, id, notification):
logger.debug(f"AddNotification({app_id}, {id}, {notification})")
self.notifications.setdefault(app_id, {})[id] = notification
@dbus.service.method(
MAIN_IFACE,
in_signature="ss",
out_signature="",
)
def RemoveNotification(self, app_id, id):
logger.debug(f"AddNotification({app_id}, {id})")
del self.notifications[app_id][id]
@dbus.service.method(
MOCK_IFACE,
in_signature="sssav",
out_signature="",
)
def EmitActionInvoked(self, app_id, id, action, parameter):
logger.debug(f"EmitActionInvoked({app_id}, {id}, {action}, {parameter})")
# n = self.notifications[app_id][id]
# FIXME check action is in n
self.EmitSignal(
MAIN_IFACE,
"ActionInvoked",
"sssav",
[
app_id,
id,
action,
dbus.Array(parameter, signature="v"),
],
)

View File

@@ -0,0 +1,119 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
#
# This file is formatted with Python Black
# mypy: disable-error-code="misc"
from tests.templates import Response, init_logger, ImplRequest
import dbus.service
from dataclasses import dataclass
BUS_NAME = "org.freedesktop.impl.portal.Test"
MAIN_OBJ = "/org/freedesktop/portal/desktop"
SYSTEM_BUS = False
MAIN_IFACE = "org.freedesktop.impl.portal.Print"
VERSION = 3
logger = init_logger(__name__)
@dataclass
class PrintParameters:
delay: int
response: int
results: dict
expect_close: bool
prepare_results: dict
def load(mock, parameters={}):
logger.debug(f"Loading parameters: {parameters}")
assert not hasattr(mock, "print_params")
mock.print_params = PrintParameters(
delay=parameters.get("delay", 200),
response=parameters.get("response", 0),
results=parameters.get("results", {}),
expect_close=parameters.get("expect-close", False),
prepare_results=parameters.get("prepare-results", {}),
)
mock.AddProperties(
MAIN_IFACE,
dbus.Dictionary(
{
"version": dbus.UInt32(parameters.get("version", VERSION)),
}
),
)
@dbus.service.method(
MAIN_IFACE,
in_signature="osssa{sv}a{sv}a{sv}",
out_signature="ua{sv}",
async_callbacks=("cb_success", "cb_error"),
)
def PreparePrint(
self,
handle,
app_id,
parent_window,
title,
settings,
page_setup,
options,
cb_success,
cb_error,
):
logger.debug(
f"PreparePrint({handle}, {app_id}, {parent_window}, {title}, {settings}, {page_setup}, {options})"
)
params = self.print_params
request = ImplRequest(
self,
BUS_NAME,
handle,
logger,
cb_success,
cb_error,
)
if params.expect_close:
request.wait_for_close()
else:
request.respond(
Response(params.response, params.prepare_results), delay=params.delay
)
@dbus.service.method(
MAIN_IFACE,
in_signature="osssha{sv}",
out_signature="ua{sv}",
async_callbacks=("cb_success", "cb_error"),
)
def Print(
self, handle, app_id, parent_window, title, fd, options, cb_success, cb_error
):
logger.debug(
f"Print({handle}, {app_id}, {parent_window}, {title}, {fd}, {options})"
)
params = self.print_params
request = ImplRequest(
self,
BUS_NAME,
handle,
logger,
cb_success,
cb_error,
)
if params.expect_close:
request.wait_for_close()
else:
request.respond(Response(params.response, params.results), delay=params.delay)

View File

@@ -0,0 +1,185 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
#
# This file is formatted with Python Black
# mypy: disable-error-code="misc"
from tests.templates import Response, init_logger, ImplRequest, ImplSession
from dbusmock import MOCK_IFACE
import dbus
import dbus.service
import socket
from gi.repository import GLib
from dataclasses import dataclass
BUS_NAME = "org.freedesktop.impl.portal.Test"
MAIN_OBJ = "/org/freedesktop/portal/desktop"
SYSTEM_BUS = False
MAIN_IFACE = "org.freedesktop.impl.portal.RemoteDesktop"
VERSION = 2
logger = init_logger(__name__)
@dataclass
class RemotedesktopParameters:
delay: int
response: int
expect_close: bool
force_close: int
force_clipoboard_enabled: bool
fail_connect_to_eis: bool
def load(mock, parameters={}):
logger.debug(f"Loading parameters: {parameters}")
assert not hasattr(mock, "remotedesktop_params")
mock.remotedesktop_params = RemotedesktopParameters(
delay=parameters.get("delay", 200),
response=parameters.get("response", 0),
expect_close=parameters.get("expect-close", False),
force_close=parameters.get("force-close", 0),
force_clipoboard_enabled=parameters.get("force-clipboard-enabled", False),
fail_connect_to_eis=parameters.get("fail-connect-to-eis", False),
)
mock.AddProperties(
MAIN_IFACE,
dbus.Dictionary(
{
"version": dbus.UInt32(parameters.get("version", VERSION)),
}
),
)
mock.sessions: dict[str, ImplSession] = {}
@dbus.service.method(
MAIN_IFACE,
in_signature="oosa{sv}",
out_signature="ua{sv}",
async_callbacks=("cb_success", "cb_error"),
)
def CreateSession(self, handle, session_handle, app_id, options, cb_success, cb_error):
logger.debug(f"CreateSession({handle}, {session_handle}, {app_id}, {options})")
params = self.remotedesktop_params
session = ImplSession(self, BUS_NAME, session_handle, app_id).export()
self.sessions[session_handle] = session
request = ImplRequest(
self,
BUS_NAME,
handle,
logger,
cb_success,
cb_error,
)
if params.expect_close:
request.wait_for_close()
else:
request.respond(
Response(params.response, {"session_handle": session.handle}),
delay=params.delay,
)
if params.force_close > 0:
GLib.timeout_add(params.force_close, session.close)
@dbus.service.method(
MAIN_IFACE,
in_signature="oosa{sv}",
out_signature="ua{sv}",
async_callbacks=("cb_success", "cb_error"),
)
def SelectDevices(self, handle, session_handle, app_id, options, cb_success, cb_error):
logger.debug(f"SelectDevices({handle}, {session_handle}, {app_id}, {options})")
params = self.remotedesktop_params
assert session_handle in self.sessions
request = ImplRequest(
self,
BUS_NAME,
handle,
logger,
cb_success,
cb_error,
)
if params.expect_close:
request.wait_for_close()
else:
request.respond(Response(params.response, {}), delay=params.delay)
@dbus.service.method(
MAIN_IFACE,
in_signature="oossa{sv}",
out_signature="ua{sv}",
async_callbacks=("cb_success", "cb_error"),
)
def Start(
self, handle, session_handle, app_id, parent_window, options, cb_success, cb_error
):
logger.debug(
f"Start({handle}, {session_handle}, {parent_window}, {app_id}, {options})"
)
params = self.remotedesktop_params
assert session_handle in self.sessions
request = ImplRequest(
self,
BUS_NAME,
handle,
logger,
cb_success,
cb_error,
)
response = Response(params.response, {})
if params.force_clipoboard_enabled:
response.results["clipboard_enabled"] = True
if params.expect_close:
request.wait_for_close()
else:
request.respond(response, delay=params.delay)
@dbus.service.method(
MAIN_IFACE,
in_signature="osa{sv}",
out_signature="h",
)
def ConnectToEIS(self, session_handle, app_id, options):
try:
logger.debug(f"ConnectToEIS({session_handle}, {app_id}, {options})")
params = self.remotedesktop_params
assert session_handle in self.sessions
if params.fail_connect_to_eis:
raise dbus.exceptions.DBusException("Purposely failing ConnectToEIS")
sockets = socket.socketpair()
self.eis_socket = sockets[0]
assert self.eis_socket.send(b"HELLO") == 5
return dbus.types.UnixFd(sockets[1])
except Exception as e:
logger.critical(e)
raise e
@dbus.service.method(MOCK_IFACE, in_signature="s", out_signature="s")
def GetSessionAppId(self, session_handle):
logger.debug(f"GetSessionAppId({session_handle})")
assert session_handle in self.sessions
return self.sessions[session_handle].app_id

View File

@@ -0,0 +1,98 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
#
# This file is formatted with Python Black
# mypy: disable-error-code="misc"
from tests.templates import Response, init_logger, ImplRequest
import dbus.service
from dataclasses import dataclass
BUS_NAME = "org.freedesktop.impl.portal.Test"
MAIN_OBJ = "/org/freedesktop/portal/desktop"
SYSTEM_BUS = False
MAIN_IFACE = "org.freedesktop.impl.portal.Screenshot"
VERSION = 2
logger = init_logger(__name__)
@dataclass
class ScreenshotParameters:
delay: int
response: int
results: dict
expect_close: bool
def load(mock, parameters={}):
logger.debug(f"Loading parameters: {parameters}")
assert not hasattr(mock, "screenshot_params")
mock.screenshot_params = ScreenshotParameters(
delay=parameters.get("delay", 200),
response=parameters.get("response", 0),
results=parameters.get("results", {}),
expect_close=parameters.get("expect-close", False),
)
mock.AddProperties(
MAIN_IFACE,
dbus.Dictionary(
{
"version": dbus.UInt32(parameters.get("version", VERSION)),
}
),
)
@dbus.service.method(
MAIN_IFACE,
in_signature="ossa{sv}",
out_signature="ua{sv}",
async_callbacks=("cb_success", "cb_error"),
)
def Screenshot(self, handle, app_id, parent_window, options, cb_success, cb_error):
logger.debug(f"Screenshot({handle}, {app_id}, {parent_window}, {options})")
params = self.screenshot_params
request = ImplRequest(
self,
BUS_NAME,
handle,
logger,
cb_success,
cb_error,
)
if params.expect_close:
request.wait_for_close()
else:
request.respond(Response(params.response, params.results), delay=params.delay)
@dbus.service.method(
MAIN_IFACE,
in_signature="ossa{sv}",
out_signature="ua{sv}",
async_callbacks=("cb_success", "cb_error"),
)
def PickColor(self, handle, app_id, parent_window, options, cb_success, cb_error):
logger.debug(f"PickColor({handle}, {app_id}, {parent_window}, {options})")
params = self.screenshot_params
request = ImplRequest(
self,
BUS_NAME,
handle,
logger,
cb_success,
cb_error,
)
if params.expect_close:
request.wait_for_close()
else:
request.respond(Response(params.response, params.results), delay=params.delay)

View File

@@ -0,0 +1,107 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
#
# This file is formatted with Python Black
# mypy: disable-error-code="misc"
from tests.templates import init_logger
import dbus.service
from dbusmock import MOCK_IFACE
from dataclasses import dataclass
BUS_NAME = "org.freedesktop.impl.portal.Test"
MAIN_OBJ = "/org/freedesktop/portal/desktop"
SYSTEM_BUS = False
MAIN_IFACE = "org.freedesktop.impl.portal.Settings"
VERSION = 2
logger = init_logger(__name__)
@dataclass
class SettingsParameters:
settings: dict
def load(mock, parameters={}):
logger.debug(f"Loading parameters: {parameters}")
assert not hasattr(mock, "settings_params")
mock.settings_params = SettingsParameters(
settings=parameters.get("settings", {}),
)
mock.AddProperties(
MAIN_IFACE,
dbus.Dictionary(
{
"version": dbus.UInt32(parameters.get("version", VERSION)),
}
),
)
@dbus.service.method(
MAIN_IFACE,
in_signature="as",
out_signature="a{sa{sv}}",
)
def ReadAll(self, namespaces):
logger.debug(f"ReadAll({namespaces})")
settings = self.settings_params.settings
if len(namespaces) == 0 or (len(namespaces) == 1 and namespaces[0] == ""):
return settings
def find_matching(namespace):
if len(namespace) >= 3 and namespace[-2:] == ".*":
ns_prefix = namespace[:-2]
matches = {}
for ns in settings:
if ns.startswith(ns_prefix):
matches[ns] = settings[ns]
return matches
if namespace in settings:
return {namespace: settings[namespace]}
return {}
result = dbus.Dictionary({}, signature="sa{sv}")
for ns in namespaces:
result |= find_matching(ns)
return result
@dbus.service.method(
MAIN_IFACE,
in_signature="ss",
out_signature="v",
)
def Read(self, namespace, key):
logger.debug(f"Read({namespace}, {key})")
settings = self.settings_params.settings
return settings[namespace][key]
@dbus.service.method(
MOCK_IFACE,
in_signature="ssv",
out_signature="",
)
def SetSetting(self, namespace, key, value):
logger.debug(f"SetSetting({namespace}, {key}, {value})")
settings = self.settings_params.settings
settings.setdefault(namespace, {})[key] = value
self.EmitSignal(
MAIN_IFACE,
"SettingChanged",
"ssv",
[namespace, key, value],
)

View File

@@ -0,0 +1,146 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
#
# This file is formatted with Python Black
# mypy: disable-error-code="misc"
from tests.templates import Response, init_logger, ImplRequest
import dbus
import dbus.service
from dbusmock import MOCK_IFACE
from dataclasses import dataclass
BUS_NAME = "org.freedesktop.impl.portal.Test"
MAIN_OBJ = "/org/freedesktop/portal/desktop"
SYSTEM_BUS = False
MAIN_IFACE = "org.freedesktop.impl.portal.Usb"
VERSION = 1
logger = init_logger(__name__)
@dataclass
class UsbParameters:
delay: int
response: int
expect_close: bool
filters: dict
def load(mock, parameters={}):
logger.debug(f"Loading parameters: {parameters}")
assert not hasattr(mock, "usb_params")
mock.usb_params = UsbParameters(
delay=parameters.get("delay", 200),
response=parameters.get("response", 0),
expect_close=parameters.get("expect-close", False),
filters=parameters.get("filters", {}),
)
mock.AddProperties(
MAIN_IFACE,
dbus.Dictionary(
{
"version": dbus.UInt32(parameters.get("version", VERSION)),
}
),
)
@dbus.service.method(
MAIN_IFACE,
in_signature="ossa(sa{sv}a{sv})a{sv}",
out_signature="ua{sv}",
async_callbacks=("cb_success", "cb_error"),
)
def AcquireDevices(
self,
handle,
parent_window,
app_id,
devices,
options,
cb_success,
cb_error,
):
logger.debug(
f"AcquireDevices({handle}, {parent_window}, {app_id}, {devices}, {options})"
)
params = self.usb_params
request = ImplRequest(
self,
BUS_NAME,
handle,
logger,
cb_success,
cb_error,
)
def reply():
# no options supported
assert not options
devices_out = []
for device in devices:
(id, info, access_options) = device
props = info["properties"]
allows_writable = params.filters.get("writable", True)
needs_writable = access_options.get("writable", False)
if needs_writable and not allows_writable:
logger.debug(f"Skipping device {id} because it requires writable")
continue
needs_vendor = params.filters.get("vendor", None)
needs_vendor = int(needs_vendor, 16) if needs_vendor else None
vendor = props.get("ID_VENDOR_ID", None)
vendor = int(vendor, 16) if vendor else None
if needs_vendor is not None and needs_vendor != vendor:
logger.debug(
f"Skipping device {id} because it does not belong to vendor {needs_vendor:02x}"
)
continue
needs_model = params.filters.get("model", None)
needs_model = int(needs_model, 16) if needs_model else None
model = props.get("ID_MODEL_ID", None)
model = int(model, 16) if model else None
if needs_model is not None and needs_model != model:
logger.debug(
f"Skipping device {id} because it is not a model {needs_model:02x}"
)
continue
devices_out.append(
dbus.Struct([id, access_options], signature="sa{sv}", variant_level=1)
)
return Response(
params.response,
{"devices": dbus.Array(devices_out, signature="(sa{sv})", variant_level=1)},
)
if params.expect_close:
request.wait_for_close()
else:
request.respond(reply, delay=params.delay)
@dbus.service.method(
MOCK_IFACE,
in_signature="a{sv}",
out_signature="",
)
def SetSelectionFilters(self, filters):
logger.debug(f"SetSelectionFilters({filters})")
params = self.usb_params
params.filters = filters

View File

@@ -0,0 +1,70 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
#
# This file is formatted with Python Black
# mypy: disable-error-code="misc"
from tests.templates import Response, init_logger, ImplRequest
import dbus
import dbus.service
from dataclasses import dataclass
BUS_NAME = "org.freedesktop.impl.portal.Test"
MAIN_OBJ = "/org/freedesktop/portal/desktop"
SYSTEM_BUS = False
MAIN_IFACE = "org.freedesktop.impl.portal.Wallpaper"
logger = init_logger(__name__)
@dataclass
class WallpaperParameters:
delay: int
response: int
expect_close: bool
def load(mock, parameters={}):
logger.debug(f"Loading parameters: {parameters}")
assert not hasattr(mock, "wallpaper_params")
mock.wallpaper_params = WallpaperParameters(
delay=parameters.get("delay", 200),
response=parameters.get("response", 0),
expect_close=parameters.get("expect-close", False),
)
@dbus.service.method(
MAIN_IFACE,
in_signature="osssa{sv}",
out_signature="u",
async_callbacks=("cb_success", "cb_error"),
)
def SetWallpaperURI(
self, handle, app_id, parent_window, uri, options, cb_success, cb_error
):
logger.debug(
f"SetWallpaperURI({handle}, {app_id}, {parent_window}, {uri}, {options})"
)
params = self.wallpaper_params
# This is irregular: the backend doesn't return the results vardict
def internal_cb_success(response, results):
cb_success(response)
request = ImplRequest(
self,
BUS_NAME,
handle,
logger,
internal_cb_success,
cb_error,
)
if params.expect_close:
request.wait_for_close()
else:
request.respond(Response(params.response, {}), delay=params.delay)