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
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:
105
Telegram/ThirdParty/xdg-desktop-portal/tests/README.md
vendored
Normal file
105
Telegram/ThirdParty/xdg-desktop-portal/tests/README.md
vendored
Normal file
@@ -0,0 +1,105 @@
|
||||
xdg-desktop-portal test suite
|
||||
=============================
|
||||
|
||||
## Unit tests
|
||||
|
||||
This directory contains a number of unit tests. The tests are written in C and
|
||||
are using the glib testing framework (https://docs.gtk.org/glib/testing.html).
|
||||
|
||||
The files follow the pattern `test-$NAME.c` and are compiled by meson. The tests
|
||||
can be run with `meson test --suite unit`.
|
||||
|
||||
## Integration tests
|
||||
|
||||
The integration tests usually test a specific portal in a fully integrated
|
||||
environment. The tests are written in python using the pytest framework.
|
||||
|
||||
The files follow the pattern `test_$NAME.py`. The tests can be run with
|
||||
`meson test --suite integration` or with `run-test.sh` in the source directory.
|
||||
|
||||
The environment is being set up by fixtures in `conftest.py` which can be
|
||||
overwritten or parameterized by the tests themselves. There are a bunch of
|
||||
convenient functions and classes in `__init__.py`. The portal backends are
|
||||
implemented using dbusmock templates in the `templates` directory.
|
||||
|
||||
### Environment
|
||||
|
||||
Some environment variables need to be set for the integration tests to function
|
||||
properly and the harness will refuse to launch if they are not set. If the
|
||||
harness is executed by meson or run-test.sh, they will be set automatically.
|
||||
|
||||
* `XDG_DESKTOP_PORTAL_PATH`: The path to the xdg-desktop-portal binary
|
||||
|
||||
* `XDG_PERMISSION_STORE_PATH`: The path to the xdg-permission-store binary
|
||||
|
||||
* `XDG_DOCUMENT_PORTAL_PATH`: The path to the xdg-document-portal binary
|
||||
|
||||
* `XDP_VALIDATE_ICON`: The path to the xdg-desktop-portal-validate-icon binary
|
||||
|
||||
* `XDP_VALIDATE_SOUND`: The path to the xdg-desktop-portal-validate-sound binary
|
||||
|
||||
* `XDP_VALIDATE_AUTO`: If set, automatically discovers the icon and sound
|
||||
validators (only useful for installed tests) instead of using
|
||||
`XDP_VALIDATE_ICON` and `XDP_VALIDATE_SOUND`.
|
||||
|
||||
Some optional environment variables that can be set to influence how the test
|
||||
harness behaves.
|
||||
|
||||
* `XDP_TEST_IN_CI`: If set (to any value), some unreliable tests might get
|
||||
skipped and some tests might run less iterations or otherwise test less
|
||||
thoroughly.
|
||||
Set this for automated QA testing, leave it unset during development.
|
||||
|
||||
* `XDP_TEST_RUN_LONG`: If set (to any value), some tests will run more
|
||||
iterations or otherwise test more thoroughly
|
||||
|
||||
* `FLATPAK_BWRAP`: Path to the **bwrap**(1) executable
|
||||
(default: discovered at build-time)
|
||||
|
||||
* `XDP_VALIDATE_ICON_INSECURE`: If set (to any value), x-d-p doesn't
|
||||
sandbox the icon validator using **bwrap**(1), even if sandboxed
|
||||
validation was enabled at compile time.
|
||||
This can be used to run build-time tests in a chroot or unprivileged
|
||||
container environment, where **bwrap**(1) normally can't work.
|
||||
It should never be set on a production system that will be validating
|
||||
untrusted icons!
|
||||
|
||||
* `XDP_VALIDATE_SOUND_INSECURE`: Same as `XDP_VALIDATE_ICON_INSECURE`,
|
||||
but for sounds
|
||||
|
||||
Some optional environment variables that can be set to help with debugging.
|
||||
|
||||
* `XDP_DBUS_MONITOR`: If set, starts dbus-monitor on the test dbus server
|
||||
|
||||
* `XDP_DBUS_TIMEOUT`: Maximum timeout for dbus calls in ms (default: 5s)
|
||||
|
||||
* `XDG_DESKTOP_PORTAL_WAIT_FOR_DEBUGGER`: Makes xdg-desktop-portal wait for
|
||||
a debugger to attach by raising SIGSTOP
|
||||
|
||||
* `XDG_DOCUMENT_PORTAL_WAIT_FOR_DEBUGGER`: Makes xdg-document-portal wait
|
||||
for a debugger to attach by raising SIGSTOP
|
||||
|
||||
* `XDG_PERMISSION_STORE_WAIT_FOR_DEBUGGER`: Makes xdg-permission-store wait
|
||||
for a debugger to attach by raising SIGSTOP
|
||||
|
||||
Internal environment variables the tests use via pytest fixtures to set up the
|
||||
environment they need.
|
||||
|
||||
* `XDG_DESKTOP_PORTAL_TEST_APP_ID`: If set, the portal will use a host
|
||||
XdpAppInfo with the app id set to the variable. This is used to get a
|
||||
predictable app id for tests.
|
||||
|
||||
* `XDG_DESKTOP_PORTAL_TEST_USB_QUERIES`: The USB queries for the USB device
|
||||
portal testing
|
||||
|
||||
### Adding new tests
|
||||
|
||||
Make sure the required portals are listed in
|
||||
`xdg_desktop_portal_dir_default_files` in `conftest.py`.
|
||||
|
||||
Add a `test_${name}.py` file to this directory and add the file to
|
||||
`meson.build`.
|
||||
|
||||
If the portal that is being tested requires a backend implementation, add
|
||||
it to the `templates` directory and add the file to `meson.build`. See the
|
||||
dbusmock documentation for details on those templates.
|
||||
620
Telegram/ThirdParty/xdg-desktop-portal/tests/__init__.py
vendored
Normal file
620
Telegram/ThirdParty/xdg-desktop-portal/tests/__init__.py
vendored
Normal file
@@ -0,0 +1,620 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
#
|
||||
# This file is formatted with Python Black
|
||||
|
||||
from dbus.mainloop.glib import DBusGMainLoop
|
||||
from gi.repository import GLib, Gio
|
||||
from itertools import count
|
||||
from typing import Any, Dict, Optional, NamedTuple, Callable, List
|
||||
|
||||
import os
|
||||
import dbus
|
||||
import dbus.proxies
|
||||
import dbusmock
|
||||
import logging
|
||||
import subprocess
|
||||
|
||||
|
||||
DBusGMainLoop(set_as_default=True)
|
||||
|
||||
# Anything that takes longer than 5s needs to fail
|
||||
DBUS_TIMEOUT = int(os.environ.get("XDP_DBUS_TIMEOUT", "5000"))
|
||||
|
||||
_counter = count()
|
||||
|
||||
ASV = Dict[str, Any]
|
||||
|
||||
|
||||
def init_logger(name: str) -> logging.Logger:
|
||||
"""
|
||||
Common logging setup for tests. Use as:
|
||||
|
||||
>>> import tests as xdp
|
||||
>>> logger = xdp.init_logger(__name__)
|
||||
>>> logger.debug("foo")
|
||||
|
||||
"""
|
||||
logging.basicConfig(
|
||||
format="%(levelname).1s|%(name)s: %(message)s", level=logging.DEBUG
|
||||
)
|
||||
logger = logging.getLogger(f"xdp.{name}")
|
||||
logger.setLevel(logging.DEBUG)
|
||||
return logger
|
||||
|
||||
|
||||
logger = init_logger("utils")
|
||||
|
||||
|
||||
def is_in_ci() -> bool:
|
||||
return os.environ.get("XDP_TEST_IN_CI") is not None
|
||||
|
||||
|
||||
def is_in_container() -> bool:
|
||||
return is_in_ci() or (
|
||||
"container" in os.environ
|
||||
and (os.environ["container"] == "docker" or os.environ["container"] == "podman")
|
||||
)
|
||||
|
||||
|
||||
def run_long_tests() -> bool:
|
||||
return os.environ.get("XDP_TEST_RUN_LONG") is not None
|
||||
|
||||
|
||||
def check_program_success(cmd) -> bool:
|
||||
proc = subprocess.Popen(
|
||||
cmd, stdout=None, stderr=None, shell=True, universal_newlines=True
|
||||
)
|
||||
_ = proc.communicate()
|
||||
return proc.returncode == 0
|
||||
|
||||
|
||||
class FuseNotSupportedException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def ensure_fuse_supported() -> None:
|
||||
if not check_program_success("fusermount3 --version"):
|
||||
raise FuseNotSupportedException("no fusermount3")
|
||||
|
||||
if not check_program_success(
|
||||
"capsh --print | grep -q 'Bounding set.*[^a-z]cap_sys_admin'"
|
||||
):
|
||||
raise FuseNotSupportedException(
|
||||
"No cap_sys_admin in bounding set, can't use FUSE"
|
||||
)
|
||||
|
||||
if not check_program_success("[ -w /dev/fuse ]"):
|
||||
raise FuseNotSupportedException("no write access to /dev/fuse")
|
||||
|
||||
if not check_program_success("[ -e /etc/mtab ]"):
|
||||
raise FuseNotSupportedException("no /etc/mtab")
|
||||
|
||||
|
||||
def wait(ms: int):
|
||||
"""
|
||||
Waits for the specified amount of milliseconds.
|
||||
"""
|
||||
mainloop = GLib.MainLoop()
|
||||
GLib.timeout_add(ms, mainloop.quit)
|
||||
mainloop.run()
|
||||
|
||||
|
||||
def wait_for(fn: Callable[[], bool]):
|
||||
"""
|
||||
Waits and dispatches to mainloop until the function fn returns true. This is
|
||||
useful in combination with a lambda which captures a variable:
|
||||
|
||||
my_var = False
|
||||
def callback():
|
||||
my_var = True
|
||||
do_something_later(callback)
|
||||
xdp.wait_for(lambda: my_var)
|
||||
"""
|
||||
mainloop = GLib.MainLoop()
|
||||
while not fn():
|
||||
GLib.timeout_add(50, mainloop.quit)
|
||||
mainloop.run()
|
||||
|
||||
|
||||
def get_permission_store_iface(bus: dbus.Bus) -> dbus.Interface:
|
||||
"""
|
||||
Returns the dbus interface of the xdg-permission-store.
|
||||
"""
|
||||
obj = bus.get_object(
|
||||
"org.freedesktop.impl.portal.PermissionStore",
|
||||
"/org/freedesktop/impl/portal/PermissionStore",
|
||||
)
|
||||
return dbus.Interface(obj, "org.freedesktop.impl.portal.PermissionStore")
|
||||
|
||||
|
||||
def get_document_portal_iface(bus: dbus.Bus) -> dbus.Interface:
|
||||
"""
|
||||
Returns the dbus interface of the xdg-document-portal.
|
||||
"""
|
||||
obj = bus.get_object(
|
||||
"org.freedesktop.portal.Documents",
|
||||
"/org/freedesktop/portal/documents",
|
||||
)
|
||||
return dbus.Interface(obj, "org.freedesktop.portal.Documents")
|
||||
|
||||
|
||||
def get_mock_iface(bus: dbus.Bus, bus_name: Optional[str] = None) -> dbus.Interface:
|
||||
"""
|
||||
Returns the mock interface of the xdg-desktop-portal.
|
||||
"""
|
||||
if not bus_name:
|
||||
bus_name = "org.freedesktop.impl.portal.Test"
|
||||
|
||||
obj = bus.get_object(bus_name, "/org/freedesktop/portal/desktop")
|
||||
return dbus.Interface(obj, dbusmock.MOCK_IFACE)
|
||||
|
||||
|
||||
def portal_interface_name(portal_name: str, domain: Optional[str] = None) -> str:
|
||||
"""
|
||||
Returns the fully qualified interface for a portal name.
|
||||
"""
|
||||
if domain:
|
||||
return f"org.freedesktop.{domain}.portal.{portal_name}"
|
||||
else:
|
||||
return f"org.freedesktop.portal.{portal_name}"
|
||||
|
||||
|
||||
def get_portal_iface(
|
||||
bus: dbus.Bus, name: str, domain: Optional[str] = None
|
||||
) -> dbus.Interface:
|
||||
"""
|
||||
Returns the dbus interface for a portal name.
|
||||
"""
|
||||
name = portal_interface_name(name, domain)
|
||||
return get_iface(bus, name)
|
||||
|
||||
|
||||
def get_iface(bus: dbus.Bus, name: str) -> dbus.Interface:
|
||||
"""
|
||||
Returns a named interface of the main portal object.
|
||||
"""
|
||||
try:
|
||||
ifaces = bus._xdp_portal_ifaces
|
||||
except AttributeError:
|
||||
ifaces = bus._xdp_portal_ifaces = {}
|
||||
|
||||
try:
|
||||
intf = ifaces[name]
|
||||
except KeyError:
|
||||
intf = dbus.Interface(get_xdp_dbus_object(bus), name)
|
||||
assert intf
|
||||
ifaces[name] = intf
|
||||
return intf
|
||||
|
||||
|
||||
def get_xdp_dbus_object(bus: dbus.Bus) -> dbus.proxies.ProxyObject:
|
||||
"""
|
||||
Returns the main portal object.
|
||||
"""
|
||||
try:
|
||||
obj = getattr(bus, "_xdp_dbus_object")
|
||||
except AttributeError:
|
||||
obj = bus.get_object(
|
||||
"org.freedesktop.portal.Desktop", "/org/freedesktop/portal/desktop"
|
||||
)
|
||||
assert obj
|
||||
bus._xdp_dbus_object = obj
|
||||
return obj
|
||||
|
||||
|
||||
def check_version(bus: dbus.Bus, portal_name: str, expected_version: int):
|
||||
"""
|
||||
Checks that the portal_name portal version is equal to expected_version.
|
||||
"""
|
||||
properties_intf = dbus.Interface(
|
||||
get_xdp_dbus_object(bus), "org.freedesktop.DBus.Properties"
|
||||
)
|
||||
portal_iface_name = portal_interface_name(portal_name)
|
||||
try:
|
||||
portal_version = properties_intf.Get(portal_iface_name, "version")
|
||||
assert int(portal_version) == expected_version
|
||||
except dbus.exceptions.DBusException as e:
|
||||
logger.critical(e)
|
||||
assert e is None, str(e)
|
||||
|
||||
|
||||
class Response(NamedTuple):
|
||||
"""
|
||||
Response as returned by a completed :class:`Request`
|
||||
"""
|
||||
|
||||
response: int
|
||||
results: ASV
|
||||
|
||||
|
||||
class ResponseTimeout(Exception):
|
||||
"""
|
||||
Exception raised by :meth:`Request.call` if the Request did not receive a
|
||||
Response in time.
|
||||
"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class Closable:
|
||||
"""
|
||||
Parent class for both Session and Request. Both of these have a Close()
|
||||
method.
|
||||
"""
|
||||
|
||||
def __init__(self, bus: dbus.Bus, objpath: str):
|
||||
self.objpath = objpath
|
||||
# GLib makes assertions in callbacks impossible, so we wrap all
|
||||
# callbacks into a try: except and store the error on the request to
|
||||
# be raised later when we're back in the main context
|
||||
self.error: Optional[Exception] = None
|
||||
|
||||
self._mainloop: Optional[GLib.MainLoop] = None
|
||||
self._impl_closed = False
|
||||
self._bus = bus
|
||||
|
||||
self._closable = type(self).__name__
|
||||
assert self._closable in ("Request", "Session")
|
||||
proxy = bus.get_object("org.freedesktop.portal.Desktop", objpath)
|
||||
self._closable_interface = dbus.Interface(
|
||||
proxy, f"org.freedesktop.portal.{self._closable}"
|
||||
)
|
||||
|
||||
@property
|
||||
def bus(self) -> dbus.Bus:
|
||||
return self._bus
|
||||
|
||||
@property
|
||||
def closed(self) -> bool:
|
||||
"""
|
||||
True if the impl.portal was closed
|
||||
"""
|
||||
return self._impl_closed
|
||||
|
||||
def close(self) -> None:
|
||||
signal_match = None
|
||||
|
||||
def cb_impl_closed_by_portal(handle) -> None:
|
||||
if handle == self.objpath:
|
||||
logger.debug(f"Impl{self._closable} {self.objpath} was closed")
|
||||
signal_match.remove() # type: ignore
|
||||
self._impl_closed = True
|
||||
if self.closed and self._mainloop:
|
||||
self._mainloop.quit()
|
||||
|
||||
# See :class:`ImplRequest`, this signal is a side-channel for the
|
||||
# impl.portal template to notify us when the impl.Request was really
|
||||
# closed by the portal.
|
||||
signal_match = self._bus.add_signal_receiver(
|
||||
cb_impl_closed_by_portal,
|
||||
f"{self._closable}Closed",
|
||||
dbus_interface="org.freedesktop.impl.portal.Mock",
|
||||
)
|
||||
|
||||
logger.debug(f"Closing {self._closable} {self.objpath}")
|
||||
self._closable_interface.Close()
|
||||
|
||||
def schedule_close(self, timeout_ms=300):
|
||||
"""
|
||||
Schedule an automatic Close() on the given timeout in milliseconds.
|
||||
"""
|
||||
assert 0 < timeout_ms < DBUS_TIMEOUT
|
||||
GLib.timeout_add(timeout_ms, self.close)
|
||||
|
||||
|
||||
class Request(Closable):
|
||||
"""
|
||||
Helper class for executing methods that use Requests. This calls takes
|
||||
care of subscribing to the signals and invokes the method on the
|
||||
interface with the expected behaviors. A typical invocation is:
|
||||
|
||||
>>> response = Request(connection, interface).call("Foo", bar="bar")
|
||||
>>> assert response.response == 0
|
||||
|
||||
Requests can only be used once, to call a second method you must
|
||||
instantiate a new Request object.
|
||||
"""
|
||||
|
||||
def __init__(self, bus: dbus.Bus, interface: dbus.Interface):
|
||||
def sanitize(name):
|
||||
return name.lstrip(":").replace(".", "_")
|
||||
|
||||
sender_token = sanitize(bus.get_unique_name())
|
||||
self._handle_token = f"request{next(_counter)}"
|
||||
self.handle = f"/org/freedesktop/portal/desktop/request/{sender_token}/{self._handle_token}"
|
||||
# The Closable
|
||||
super().__init__(bus, self.handle)
|
||||
|
||||
self.interface = interface
|
||||
self.response: Optional[Response] = None
|
||||
self.used = False
|
||||
# GLib makes assertions in callbacks impossible, so we wrap all
|
||||
# callbacks into a try: except and store the error on the request to
|
||||
# be raised later when we're back in the main context
|
||||
self.error: Optional[Exception] = None
|
||||
|
||||
proxy = bus.get_object("org.freedesktop.portal.Desktop", self.handle)
|
||||
self.mock_interface = dbus.Interface(proxy, dbusmock.MOCK_IFACE)
|
||||
self._proxy = bus.get_object("org.freedesktop.portal.Desktop", self.handle)
|
||||
|
||||
def cb_response(response: int, results: ASV) -> None:
|
||||
try:
|
||||
logger.debug(f"Response received on {self.handle}")
|
||||
assert self.response is None
|
||||
self.response = Response(response, results)
|
||||
if self._mainloop:
|
||||
self._mainloop.quit()
|
||||
except Exception as e:
|
||||
self.error = e
|
||||
|
||||
self.request_interface = dbus.Interface(proxy, "org.freedesktop.portal.Request")
|
||||
self.request_interface.connect_to_signal("Response", cb_response)
|
||||
|
||||
@property
|
||||
def handle_token(self) -> dbus.String:
|
||||
"""
|
||||
Returns the dbus-ready handle_token, ready to be put into the options
|
||||
"""
|
||||
return dbus.String(self._handle_token, variant_level=1)
|
||||
|
||||
def call(self, methodname: str, **kwargs) -> Optional[Response]:
|
||||
"""
|
||||
Semi-synchronously call method ``methodname`` on the interface given
|
||||
in the Request's constructor. The kwargs must be specified in the
|
||||
order the DBus method takes them but the handle_token is automatically
|
||||
filled in.
|
||||
|
||||
>>> response = Request(connection, interface).call("Foo", bar="bar")
|
||||
>>> if response.response != 0:
|
||||
... print("some error occured")
|
||||
|
||||
The DBus call itself is asynchronous (required for signals to work)
|
||||
but this method does not return until the Response is received, the
|
||||
Request is closed or an error occurs. If the Request is closed, the
|
||||
Response is None.
|
||||
|
||||
If the "reply_handler" and "error_handler" keywords are present, those
|
||||
callbacks are called just like they would be as dbus.service.ProxyObject.
|
||||
"""
|
||||
assert not self.used
|
||||
self.used = True
|
||||
|
||||
# Make sure options exists and has the handle_token set
|
||||
try:
|
||||
options = kwargs["options"]
|
||||
except KeyError:
|
||||
options = dbus.Dictionary({}, signature="sv")
|
||||
|
||||
if "handle_token" not in options:
|
||||
options["handle_token"] = self.handle_token
|
||||
|
||||
# Anything that takes longer than 5s needs to fail
|
||||
self._mainloop = GLib.MainLoop()
|
||||
GLib.timeout_add(DBUS_TIMEOUT, self._mainloop.quit)
|
||||
|
||||
method = getattr(self.interface, methodname)
|
||||
assert method
|
||||
|
||||
reply_handler = kwargs.pop("reply_handler", None)
|
||||
error_handler = kwargs.pop("error_handler", None)
|
||||
|
||||
# Handle the normal method reply which returns is the Request object
|
||||
# path. We don't exit the mainloop here, we're waiting for either the
|
||||
# Response signal on the Request itself or the Close() handling
|
||||
def reply_cb(handle):
|
||||
try:
|
||||
logger.debug(f"Reply to {methodname} with {self.handle}")
|
||||
assert handle == self.handle
|
||||
|
||||
if reply_handler:
|
||||
reply_handler(handle)
|
||||
except Exception as e:
|
||||
self.error = e
|
||||
|
||||
# Handle any exceptions during the actual method call (not the Request
|
||||
# handling itself). Can exit the mainloop if that happens
|
||||
def error_cb(error):
|
||||
try:
|
||||
logger.debug(f"Error after {methodname} with {error}")
|
||||
if error_handler:
|
||||
error_handler(error)
|
||||
self.error = error
|
||||
except Exception as e:
|
||||
self.error = e
|
||||
finally:
|
||||
if self._mainloop:
|
||||
self._mainloop.quit()
|
||||
|
||||
# Method is invoked async, otherwise we can't mix and match signals
|
||||
# and other calls. It's still sync as seen by the caller in that we
|
||||
# have a mainloop that waits for us to finish though.
|
||||
method(
|
||||
*list(kwargs.values()),
|
||||
reply_handler=reply_cb,
|
||||
error_handler=error_cb,
|
||||
)
|
||||
|
||||
self._mainloop.run()
|
||||
|
||||
if self.error:
|
||||
raise self.error
|
||||
elif not self.closed and self.response is None:
|
||||
raise ResponseTimeout(f"Timed out waiting for response from {methodname}")
|
||||
|
||||
return self.response
|
||||
|
||||
|
||||
class Session(Closable):
|
||||
"""
|
||||
Helper class for a Session created by a portal. This class takes care of
|
||||
subscribing to the `Closed` signals. A typical invocation is:
|
||||
|
||||
>>> response = Request(connection, interface).call("CreateSession")
|
||||
>>> session = Session.from_response(response)
|
||||
# Now run the main loop and do other stuff
|
||||
# Check if the session was closed
|
||||
>>> if session.closed:
|
||||
... pass
|
||||
# or close the session explicitly
|
||||
>>> session.close() # to close the session or
|
||||
"""
|
||||
|
||||
def __init__(self, bus: dbus.Bus, handle: str):
|
||||
assert handle
|
||||
super().__init__(bus, handle)
|
||||
|
||||
self.handle = handle
|
||||
self.details = None
|
||||
# GLib makes assertions in callbacks impossible, so we wrap all
|
||||
# callbacks into a try: except and store the error on the request to
|
||||
# be raised later when we're back in the main context
|
||||
self.error = None
|
||||
self._closed_sig_received = False
|
||||
|
||||
def cb_closed(details: ASV) -> None:
|
||||
try:
|
||||
logger.debug(f"Session.Closed received on {self.handle}")
|
||||
assert not self._closed_sig_received
|
||||
self._closed_sig_received = True
|
||||
self.details = details
|
||||
if self._mainloop:
|
||||
self._mainloop.quit()
|
||||
except Exception as e:
|
||||
self.error = e
|
||||
|
||||
proxy = bus.get_object("org.freedesktop.portal.Desktop", handle)
|
||||
self.session_interface = dbus.Interface(proxy, "org.freedesktop.portal.Session")
|
||||
self.session_interface.connect_to_signal("Closed", cb_closed)
|
||||
|
||||
@property
|
||||
def closed(self):
|
||||
"""
|
||||
Returns True if the session was closed by the backend
|
||||
"""
|
||||
return self._closed_sig_received or super().closed
|
||||
|
||||
@classmethod
|
||||
def from_response(cls, bus: dbus.Bus, response: Response) -> "Session":
|
||||
return cls(bus, response.results["session_handle"])
|
||||
|
||||
|
||||
class GDBusIfaceSignal:
|
||||
"""
|
||||
Helper class which represents a connected signal on a GDBusIface and can be
|
||||
used to disconnect from the signal.
|
||||
"""
|
||||
|
||||
def __init__(self, signal_id: int, proxy: Gio.DBusProxy):
|
||||
self.signal_id = signal_id
|
||||
self.proxy = proxy
|
||||
|
||||
def disconnect(self):
|
||||
"""
|
||||
Disconnects the signal
|
||||
"""
|
||||
self.proxy.disconnect(self.signal_id)
|
||||
|
||||
|
||||
class GDBusIface:
|
||||
"""
|
||||
Helper class for calling dbus interfaces with complex arguments.
|
||||
Usually you want to use python-dbus on the dbus_con fixture with
|
||||
get_portal_iface , get_mock_iface or get_iface. This is convenient but
|
||||
might not be sufficient for complex arguments or for asynchronously calling
|
||||
a method.
|
||||
"""
|
||||
|
||||
def __init__(self, bus: str, obj: str, iface: str):
|
||||
"""
|
||||
Creates a GDBusIface for a specific bus, object and interface on the
|
||||
session bus.
|
||||
"""
|
||||
address = Gio.dbus_address_get_for_bus_sync(Gio.BusType.SESSION, None)
|
||||
session_bus = Gio.DBusConnection.new_for_address_sync(
|
||||
address,
|
||||
Gio.DBusConnectionFlags.AUTHENTICATION_CLIENT
|
||||
| Gio.DBusConnectionFlags.MESSAGE_BUS_CONNECTION,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
assert session_bus
|
||||
self._proxy = Gio.DBusProxy.new_sync(
|
||||
session_bus,
|
||||
Gio.DBusProxyFlags.NONE,
|
||||
None,
|
||||
bus,
|
||||
obj,
|
||||
iface,
|
||||
None,
|
||||
)
|
||||
|
||||
def _call(
|
||||
self, method_name: str, args_variant: GLib.Variant, fds: List[int] = []
|
||||
) -> GLib.Variant:
|
||||
"""
|
||||
Calls a method synchronously with the arguments passed in args_variant,
|
||||
passing the file descriptors specified in fds.
|
||||
Returns the result of the dbus call.
|
||||
"""
|
||||
fdlist = Gio.UnixFDList.new()
|
||||
for fd in fds:
|
||||
fdlist.append(fd)
|
||||
|
||||
return self._proxy.call_with_unix_fd_list_sync(
|
||||
method_name,
|
||||
args_variant,
|
||||
0,
|
||||
-1,
|
||||
fdlist,
|
||||
None,
|
||||
)
|
||||
|
||||
def _call_async(
|
||||
self,
|
||||
method_name: str,
|
||||
args_variant: GLib.Variant,
|
||||
fds: List[int] = [],
|
||||
cb: Optional[Callable[[GLib.Variant], None]] = None,
|
||||
) -> None:
|
||||
"""
|
||||
Calls a method asynchronously with the arguments passed in args_variant,
|
||||
passing the file descriptors specified in fds.
|
||||
Invokes the callback cb when the call finished.
|
||||
"""
|
||||
fdlist = Gio.UnixFDList.new()
|
||||
for fd in fds:
|
||||
fdlist.append(fd)
|
||||
|
||||
def internal_cb(s, res, _):
|
||||
res = s.call_finish(res)
|
||||
if cb:
|
||||
cb(res)
|
||||
|
||||
self._proxy.call_with_unix_fd_list(
|
||||
method_name,
|
||||
args_variant,
|
||||
0,
|
||||
-1,
|
||||
fdlist,
|
||||
None,
|
||||
internal_cb,
|
||||
None,
|
||||
)
|
||||
|
||||
def connect_to_signal(
|
||||
self, name: str, cb: Callable[[GLib.Variant], None]
|
||||
) -> GDBusIfaceSignal:
|
||||
"""
|
||||
Connects to the dbus signal name to the callback cb. Returns an object
|
||||
representing the connection which can be used to disconnect it again.
|
||||
"""
|
||||
|
||||
def internal_cb(proxy, sender_name, signal_name, parameters):
|
||||
if signal_name != name:
|
||||
return
|
||||
cb(parameters)
|
||||
|
||||
signal_id = self._proxy.connect("g-signal", internal_cb)
|
||||
return GDBusIfaceSignal(signal_id, self._proxy)
|
||||
8
Telegram/ThirdParty/xdg-desktop-portal/tests/asan.suppression
vendored
Normal file
8
Telegram/ThirdParty/xdg-desktop-portal/tests/asan.suppression
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
# external (GIO?)
|
||||
leak:g_dbus_message_new_from_blob
|
||||
# Bugs in our code
|
||||
# Take a look at them and try to figure out what's going on!
|
||||
leak:permission_db_entry_set_app_permissions
|
||||
leak:test_color_delay
|
||||
leak:test_color_basic
|
||||
leak:test_color_parallel
|
||||
590
Telegram/ThirdParty/xdg-desktop-portal/tests/conftest.py
vendored
Normal file
590
Telegram/ThirdParty/xdg-desktop-portal/tests/conftest.py
vendored
Normal file
@@ -0,0 +1,590 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
#
|
||||
# This file is formatted with Python Black
|
||||
|
||||
from typing import Any, Dict, Iterator, Optional
|
||||
from types import ModuleType
|
||||
|
||||
import pytest
|
||||
import dbus
|
||||
import dbusmock
|
||||
import os
|
||||
import sys
|
||||
import tempfile
|
||||
import subprocess
|
||||
import time
|
||||
import signal
|
||||
from pathlib import Path
|
||||
from contextlib import chdir
|
||||
|
||||
import gi
|
||||
|
||||
gi.require_version("UMockdev", "1.0")
|
||||
from gi.repository import UMockdev # noqa E402
|
||||
|
||||
|
||||
def pytest_configure() -> None:
|
||||
ensure_environment_set()
|
||||
ensure_umockdev_loaded()
|
||||
|
||||
|
||||
def pytest_sessionfinish(session, exitstatus):
|
||||
# Meson and ginsttest-runner expect tests to exit with status 77 if all
|
||||
# tests were skipped
|
||||
if exitstatus == pytest.ExitCode.NO_TESTS_COLLECTED:
|
||||
session.exitstatus = 77
|
||||
|
||||
|
||||
def ensure_environment_set() -> None:
|
||||
env_vars = [
|
||||
"XDG_DESKTOP_PORTAL_PATH",
|
||||
"XDG_PERMISSION_STORE_PATH",
|
||||
"XDG_DOCUMENT_PORTAL_PATH",
|
||||
]
|
||||
|
||||
if not os.getenv("XDP_VALIDATE_AUTO"):
|
||||
env_vars += [
|
||||
"XDP_VALIDATE_ICON",
|
||||
"XDP_VALIDATE_SOUND",
|
||||
]
|
||||
else:
|
||||
os.environ.pop("XDP_VALIDATE_ICON", None)
|
||||
os.environ.pop("XDP_VALIDATE_SOUND", None)
|
||||
|
||||
for env_var in env_vars:
|
||||
if not os.getenv(env_var):
|
||||
raise Exception(f"{env_var} must be set")
|
||||
|
||||
|
||||
def ensure_umockdev_loaded() -> None:
|
||||
umockdev_preload = "libumockdev-preload.so"
|
||||
preload = os.environ.get("LD_PRELOAD", "")
|
||||
if umockdev_preload not in preload:
|
||||
os.environ["LD_PRELOAD"] = f"{umockdev_preload}:{preload}"
|
||||
os.execv(sys.executable, [sys.executable] + sys.argv)
|
||||
|
||||
|
||||
def test_dir() -> Path:
|
||||
return Path(__file__).resolve().parent
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def xdg_desktop_portal_path() -> Path:
|
||||
return Path(os.environ["XDG_DESKTOP_PORTAL_PATH"])
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def xdg_permission_store_path() -> Path:
|
||||
return Path(os.environ["XDG_PERMISSION_STORE_PATH"])
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def xdg_document_portal_path() -> Path:
|
||||
return Path(os.environ["XDG_DOCUMENT_PORTAL_PATH"])
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def create_test_dirs(umockdev: Optional[UMockdev.Testbed]) -> Iterator[None]:
|
||||
# The umockdev argument is to make sure the testbed
|
||||
# is created before we create the tmpdir
|
||||
env_dirs = [
|
||||
"HOME",
|
||||
"TMPDIR",
|
||||
"XDG_CACHE_HOME",
|
||||
"XDG_CONFIG_HOME",
|
||||
"XDG_DATA_HOME",
|
||||
"XDG_RUNTIME_DIR",
|
||||
"XDG_DESKTOP_PORTAL_DIR",
|
||||
]
|
||||
|
||||
test_root = tempfile.TemporaryDirectory(
|
||||
prefix="xdp-testroot-", ignore_cleanup_errors=True
|
||||
)
|
||||
|
||||
for env_dir in env_dirs:
|
||||
directory = Path(test_root.name) / env_dir.lower()
|
||||
directory.mkdir(mode=0o700, parents=True)
|
||||
os.environ[env_dir] = directory.absolute().as_posix()
|
||||
|
||||
yield
|
||||
|
||||
test_root.cleanup()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def xdg_data_home_files() -> Dict[str, bytes]:
|
||||
"""
|
||||
Default fixture which can be used to create files in the temporary
|
||||
XDG_DATA_HOME directory of the test.
|
||||
"""
|
||||
return {}
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def ensure_xdg_data_home(
|
||||
create_test_dirs: Any, xdg_data_home_files: Dict[str, bytes]
|
||||
) -> None:
|
||||
files = xdg_data_home_files
|
||||
for name, content in files.items():
|
||||
file_path = Path(os.environ["XDG_DATA_HOME"]) / name
|
||||
file_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
with open(file_path.absolute().as_posix(), "wb") as f:
|
||||
f.write(content)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def xdg_desktop_portal_dir_files() -> Dict[str, bytes]:
|
||||
"""
|
||||
Default fixture which can be used to create files in the temporary
|
||||
XDG_DESKTOP_PORTAL_DIR directory of the test.
|
||||
"""
|
||||
return {}
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def xdg_desktop_portal_dir_default_files() -> Dict[str, bytes]:
|
||||
files = {}
|
||||
|
||||
portals = [
|
||||
"org.freedesktop.impl.portal.Access",
|
||||
"org.freedesktop.impl.portal.Account",
|
||||
"org.freedesktop.impl.portal.AppChooser",
|
||||
"org.freedesktop.impl.portal.Background",
|
||||
"org.freedesktop.impl.portal.Clipboard",
|
||||
"org.freedesktop.impl.portal.DynamicLauncher",
|
||||
"org.freedesktop.impl.portal.Email",
|
||||
"org.freedesktop.impl.portal.FileChooser",
|
||||
"org.freedesktop.impl.portal.GlobalShortcuts",
|
||||
"org.freedesktop.impl.portal.Inhibit",
|
||||
"org.freedesktop.impl.portal.InputCapture",
|
||||
"org.freedesktop.impl.portal.Lockdown",
|
||||
"org.freedesktop.impl.portal.Notification",
|
||||
"org.freedesktop.impl.portal.Print",
|
||||
"org.freedesktop.impl.portal.RemoteDesktop",
|
||||
"org.freedesktop.impl.portal.Screenshot",
|
||||
"org.freedesktop.impl.portal.Settings",
|
||||
"org.freedesktop.impl.portal.Usb",
|
||||
"org.freedesktop.impl.portal.Wallpaper",
|
||||
]
|
||||
|
||||
files["test-portals.conf"] = b"""
|
||||
[preferred]
|
||||
default=test;
|
||||
"""
|
||||
|
||||
files["test.portal"] = """
|
||||
[portal]
|
||||
DBusName=org.freedesktop.impl.portal.Test
|
||||
Interfaces={}
|
||||
""".format(";".join(portals)).encode("utf-8")
|
||||
|
||||
return files
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def ensure_xdg_desktop_portal_dir(
|
||||
create_test_dirs: Any,
|
||||
xdg_desktop_portal_dir_files: Dict[str, bytes],
|
||||
xdg_desktop_portal_dir_default_files: Dict[str, bytes],
|
||||
) -> None:
|
||||
files = xdg_desktop_portal_dir_default_files | xdg_desktop_portal_dir_files
|
||||
for name, content in files.items():
|
||||
file_path = Path(os.environ["XDG_DESKTOP_PORTAL_DIR"]) / name
|
||||
file_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
with open(file_path.absolute().as_posix(), "wb") as f:
|
||||
f.write(content)
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def create_test_dbus() -> Iterator[dbusmock.DBusTestCase]:
|
||||
bus = dbusmock.DBusTestCase()
|
||||
bus.setUp()
|
||||
bus.start_session_bus()
|
||||
bus.start_system_bus()
|
||||
|
||||
yield bus
|
||||
|
||||
bus.tearDown()
|
||||
bus.tearDownClass()
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def create_dbus_monitor(create_test_dbus) -> Iterator[Optional[subprocess.Popen]]:
|
||||
if not os.getenv("XDP_DBUS_MONITOR"):
|
||||
yield None
|
||||
return
|
||||
|
||||
dbus_monitor = subprocess.Popen(["dbus-monitor", "--session"])
|
||||
|
||||
yield dbus_monitor
|
||||
|
||||
dbus_monitor.terminate()
|
||||
dbus_monitor.wait()
|
||||
|
||||
|
||||
def _get_server_for_module(
|
||||
busses: dict[dbusmock.BusType, dict[str, dbusmock.SpawnedMock]],
|
||||
module: ModuleType,
|
||||
bustype: dbusmock.BusType,
|
||||
) -> dbusmock.SpawnedMock:
|
||||
assert bustype in dbusmock.BusType
|
||||
|
||||
try:
|
||||
return busses[bustype][module.BUS_NAME]
|
||||
except KeyError:
|
||||
server = dbusmock.SpawnedMock.spawn_for_name(
|
||||
module.BUS_NAME,
|
||||
"/dbusmock",
|
||||
dbusmock.OBJECT_MANAGER_IFACE,
|
||||
bustype,
|
||||
stdout=None,
|
||||
stderr=None,
|
||||
)
|
||||
|
||||
busses[bustype][module.BUS_NAME] = server
|
||||
return server
|
||||
|
||||
|
||||
def _get_main_obj_for_module(
|
||||
server: dbusmock.SpawnedMock, module: ModuleType, bustype: dbusmock.BusType
|
||||
) -> dbusmock.DBusMockObject:
|
||||
try:
|
||||
server.obj.AddObject(
|
||||
module.MAIN_OBJ,
|
||||
"com.example.EmptyInterface",
|
||||
{},
|
||||
[],
|
||||
dbus_interface=dbusmock.MOCK_IFACE,
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
bustype.wait_for_bus_object(module.BUS_NAME, module.MAIN_OBJ)
|
||||
bus = bustype.get_connection()
|
||||
return bus.get_object(module.BUS_NAME, module.MAIN_OBJ)
|
||||
|
||||
|
||||
def _terminate_mock_p(process: subprocess.Popen) -> None:
|
||||
process.terminate()
|
||||
process.wait()
|
||||
|
||||
|
||||
def _terminate_servers(
|
||||
busses: dict[dbusmock.BusType, dict[str, dbusmock.SpawnedMock]],
|
||||
) -> None:
|
||||
for server in busses[dbusmock.BusType.SYSTEM].values():
|
||||
_terminate_mock_p(server.process)
|
||||
for server in busses[dbusmock.BusType.SESSION].values():
|
||||
_terminate_mock_p(server.process)
|
||||
|
||||
|
||||
def _start_template(
|
||||
busses: dict[dbusmock.BusType, dict[str, dbusmock.SpawnedMock]],
|
||||
template: str,
|
||||
bus_name: Optional[str],
|
||||
params: Dict[str, Any] = {},
|
||||
) -> None:
|
||||
"""
|
||||
Start the template and potentially start a server for it
|
||||
"""
|
||||
module_path_dir = (test_dir()).parent.absolute().as_posix()
|
||||
template_path = test_dir() / f"templates/{template.lower()}.py"
|
||||
template = template_path.absolute().as_posix()
|
||||
|
||||
# we cd to the parent dir of the test_dir so that the module search path for
|
||||
# the templates is the same as opening the modules from here
|
||||
with chdir(module_path_dir):
|
||||
module = dbusmock.mockobject.load_module(template)
|
||||
bustype = (
|
||||
dbusmock.BusType.SYSTEM if module.SYSTEM_BUS else dbusmock.BusType.SESSION
|
||||
)
|
||||
|
||||
if bus_name:
|
||||
module.BUS_NAME = bus_name
|
||||
|
||||
server = _get_server_for_module(busses, module, bustype)
|
||||
main_obj = _get_main_obj_for_module(server, module, bustype)
|
||||
|
||||
main_obj.AddTemplate(
|
||||
template,
|
||||
dbus.Dictionary(params, signature="sv"),
|
||||
dbus_interface=dbusmock.MOCK_IFACE,
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def template_params() -> dict[str, dict[str, Any]]:
|
||||
"""
|
||||
Default fixture for overriding the parameters which should be passed to the
|
||||
mocking templates. Use required_templates to specify the default parameters
|
||||
and override it for specific test cases via
|
||||
|
||||
@pytest.mark.parametrize("template_params", ({"Template": {"foo": "bar"}},))
|
||||
|
||||
"""
|
||||
return {}
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def required_templates() -> dict[str, dict[str, Any]]:
|
||||
"""
|
||||
Default fixture for enumerating the mocking templates the test case requires
|
||||
to be started. This is a map from a template spec to the parameters which
|
||||
should be passed to the template. The template spec is the name of a
|
||||
template in the templates directory and an optional dbus bus name to start
|
||||
the template at, separated by a colon.
|
||||
|
||||
This starts the `settings` and `email` templates on the bus names specified
|
||||
with BUS_NAME in the templates and passes parameters to settings.
|
||||
|
||||
{
|
||||
"settings": {
|
||||
"some_param": true,
|
||||
},
|
||||
"email": {},
|
||||
}
|
||||
|
||||
This starts two instances of the settings template with their own parameters
|
||||
once on the bus name specified in BUS_NAME in the template, and once on the
|
||||
bus name `org.freedesktop.impl.portal.OtherImpl`.
|
||||
|
||||
{
|
||||
"settings": {
|
||||
"some_param": true,
|
||||
},
|
||||
"settings:org.freedesktop.impl.portal.OtherImpl": {
|
||||
"some_param": false,
|
||||
},
|
||||
}
|
||||
|
||||
"""
|
||||
return {}
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def templates(
|
||||
required_templates: dict[str, dict[str, Any]],
|
||||
template_params: dict[str, dict[str, Any]],
|
||||
) -> Iterator[None]:
|
||||
"""
|
||||
Fixture which starts the required templates with their parameters. Usually
|
||||
the `portals` fixture is what you're looking for because it also starts
|
||||
the portal frontend and the permission store.
|
||||
"""
|
||||
busses: dict[dbusmock.BusType, dict[str, dbusmock.SpawnedMock]] = {
|
||||
dbusmock.BusType.SYSTEM: {},
|
||||
dbusmock.BusType.SESSION: {},
|
||||
}
|
||||
for template_data, params in required_templates.items():
|
||||
template, bus_name = (template_data.split(":") + [None])[:2]
|
||||
assert template
|
||||
params = template_params.get(template, params)
|
||||
_start_template(busses, template, bus_name, params)
|
||||
yield
|
||||
_terminate_servers(busses)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def xdp_overwrite_env() -> dict[str, str]:
|
||||
"""
|
||||
Default fixture which can be used to override the environment that gets
|
||||
passed to xdg-desktop-portal, xdg-document-portal and xdg-permission-store.
|
||||
"""
|
||||
return {}
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def app_id() -> str:
|
||||
"""
|
||||
Default fixture which can be used to override the app id that the portal
|
||||
frontend will discover for incoming connections.
|
||||
"""
|
||||
return "org.example.Test"
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def xdp_env(
|
||||
xdp_overwrite_env: dict[str, str],
|
||||
app_id: str,
|
||||
usb_queries: Optional[str],
|
||||
umockdev: Optional[UMockdev.Testbed],
|
||||
) -> dict[str, str]:
|
||||
env = os.environ.copy()
|
||||
env["G_DEBUG"] = "fatal-criticals"
|
||||
env["XDG_CURRENT_DESKTOP"] = "test"
|
||||
|
||||
if app_id:
|
||||
env["XDG_DESKTOP_PORTAL_TEST_APP_ID"] = app_id
|
||||
|
||||
if usb_queries:
|
||||
env["XDG_DESKTOP_PORTAL_TEST_USB_QUERIES"] = usb_queries
|
||||
|
||||
if umockdev:
|
||||
env["UMOCKDEV_DIR"] = umockdev.get_root_dir()
|
||||
|
||||
asan_suppression = test_dir() / "asan.suppression"
|
||||
if not asan_suppression.exists():
|
||||
raise FileNotFoundError(f"{asan_suppression} does not exist")
|
||||
env["LSAN_OPTIONS"] = f"suppressions={asan_suppression}"
|
||||
|
||||
for key, val in xdp_overwrite_env.items():
|
||||
env[key] = val
|
||||
|
||||
return env
|
||||
|
||||
|
||||
def _maybe_add_asan_preload(executable: Path, env: dict[str, str]) -> None:
|
||||
# ASAN really wants to be the first library to get loaded but we also
|
||||
# LD_PRELOAD umockdev and LD_PRELOAD gets loaded before any "normally"
|
||||
# linked libraries. This uses ldd to find the version of libasan.so that
|
||||
# should be loaded and puts it in front of LD_PRELOAD.
|
||||
# This way, LD_PRELOAD and ASAN can be used at the same time.
|
||||
ldd = subprocess.check_output(["ldd", executable]).decode("utf-8")
|
||||
libs = [line.split()[0] for line in ldd.splitlines()]
|
||||
try:
|
||||
libasan = next(filter(lambda lib: lib.startswith("libasan"), libs))
|
||||
except StopIteration:
|
||||
return
|
||||
|
||||
preload = env.get("LD_PRELOAD", "")
|
||||
env["LD_PRELOAD"] = f"{libasan}:{preload}"
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def xdg_desktop_portal(
|
||||
dbus_con: dbus.Bus, xdg_desktop_portal_path: Path, xdp_env: dict[str, str]
|
||||
) -> Iterator[subprocess.Popen]:
|
||||
"""
|
||||
Fixture which starts and eventually stops xdg-desktop-portal
|
||||
"""
|
||||
if not xdg_desktop_portal_path.exists():
|
||||
raise FileNotFoundError(f"{xdg_desktop_portal_path} does not exist")
|
||||
|
||||
env = xdp_env.copy()
|
||||
_maybe_add_asan_preload(xdg_desktop_portal_path, env)
|
||||
|
||||
xdg_desktop_portal = subprocess.Popen([xdg_desktop_portal_path], env=env)
|
||||
|
||||
while not dbus_con.name_has_owner("org.freedesktop.portal.Desktop"):
|
||||
returncode = xdg_desktop_portal.poll()
|
||||
if returncode is not None:
|
||||
raise subprocess.SubprocessError(
|
||||
f"xdg-desktop-portal exited with {returncode}"
|
||||
)
|
||||
time.sleep(0.1)
|
||||
|
||||
yield xdg_desktop_portal
|
||||
|
||||
xdg_desktop_portal.send_signal(signal.SIGHUP)
|
||||
returncode = xdg_desktop_portal.wait()
|
||||
assert returncode == 0
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def xdg_permission_store(
|
||||
dbus_con: dbus.Bus, xdg_permission_store_path: Path, xdp_env: dict[str, str]
|
||||
) -> Iterator[subprocess.Popen]:
|
||||
"""
|
||||
Fixture which starts and eventually stops xdg-permission-store
|
||||
"""
|
||||
if not xdg_permission_store_path.exists():
|
||||
raise FileNotFoundError(f"{xdg_permission_store_path} does not exist")
|
||||
|
||||
env = xdp_env.copy()
|
||||
_maybe_add_asan_preload(xdg_permission_store_path, env)
|
||||
|
||||
permission_store = subprocess.Popen([xdg_permission_store_path], env=env)
|
||||
|
||||
while not dbus_con.name_has_owner("org.freedesktop.impl.portal.PermissionStore"):
|
||||
returncode = permission_store.poll()
|
||||
if returncode is not None:
|
||||
raise subprocess.SubprocessError(
|
||||
f"xdg-permission-store exited with {returncode}"
|
||||
)
|
||||
time.sleep(0.1)
|
||||
|
||||
yield permission_store
|
||||
|
||||
permission_store.send_signal(signal.SIGHUP)
|
||||
permission_store.wait()
|
||||
# The permission store does not shut down cleanly currently
|
||||
# returncode = permission_store.wait()
|
||||
# assert returncode == 0
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def xdg_document_portal(
|
||||
dbus_con: dbus.Bus, xdg_document_portal_path: Path, xdp_env: dict[str, str]
|
||||
) -> Iterator[subprocess.Popen]:
|
||||
"""
|
||||
Fixture which starts and eventually stops xdg-document-portal
|
||||
"""
|
||||
if not xdg_document_portal_path.exists():
|
||||
raise FileNotFoundError(f"{xdg_document_portal_path} does not exist")
|
||||
|
||||
# FUSE and LD_PRELOAD don't like each other. Not sure what exactly is going
|
||||
# wrong but it usually just results in a weird hang that needs SIGKILL
|
||||
env = xdp_env.copy()
|
||||
env.pop("LD_PRELOAD", None)
|
||||
|
||||
document_portal = subprocess.Popen([xdg_document_portal_path], env=env)
|
||||
|
||||
while not dbus_con.name_has_owner("org.freedesktop.portal.Documents"):
|
||||
returncode = document_portal.poll()
|
||||
if returncode is not None:
|
||||
raise subprocess.SubprocessError(
|
||||
f"xdg-document-portal exited with {returncode}"
|
||||
)
|
||||
time.sleep(0.1)
|
||||
|
||||
yield document_portal
|
||||
|
||||
document_portal.send_signal(signal.SIGHUP)
|
||||
returncode = document_portal.wait()
|
||||
assert returncode == 0
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def portals(templates: Any, xdg_desktop_portal: Any, xdg_permission_store: Any) -> None:
|
||||
"""
|
||||
Fixture which starts the required templates, xdg-desktop-portal,
|
||||
xdg-document-portal and xdg-permission-store. Most tests require this.
|
||||
"""
|
||||
return None
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def usb_queries() -> Optional[str]:
|
||||
"""
|
||||
Default fixture providing the usb queries the connecting process can
|
||||
enumerate
|
||||
"""
|
||||
return None
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def umockdev() -> Optional[UMockdev.Testbed]:
|
||||
"""
|
||||
Default fixture providing a umockdev testbed
|
||||
"""
|
||||
return None
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def dbus_con(create_test_dbus: dbusmock.DBusTestCase) -> dbus.Bus:
|
||||
"""
|
||||
Default fixture which provides the python-dbus session bus of the test.
|
||||
"""
|
||||
con = create_test_dbus.get_dbus(system_bus=False)
|
||||
assert con
|
||||
return con
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def dbus_con_sys(create_test_dbus: dbusmock.DBusTestCase) -> dbus.Bus:
|
||||
"""
|
||||
Default fixture which provides the python-dbus system bus of the test.
|
||||
"""
|
||||
con_sys = create_test_dbus.get_dbus(system_bus=True)
|
||||
assert con_sys
|
||||
return con_sys
|
||||
4
Telegram/ThirdParty/xdg-desktop-portal/tests/dbs/meson.build
vendored
Normal file
4
Telegram/ThirdParty/xdg-desktop-portal/tests/dbs/meson.build
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
configure_file(input: 'no_tables',
|
||||
output: '@PLAINNAME@',
|
||||
copy: true
|
||||
)
|
||||
BIN
Telegram/ThirdParty/xdg-desktop-portal/tests/dbs/no_tables
vendored
Normal file
BIN
Telegram/ThirdParty/xdg-desktop-portal/tests/dbs/no_tables
vendored
Normal file
Binary file not shown.
191
Telegram/ThirdParty/xdg-desktop-portal/tests/meson.build
vendored
Normal file
191
Telegram/ThirdParty/xdg-desktop-portal/tests/meson.build
vendored
Normal file
@@ -0,0 +1,191 @@
|
||||
env_tests = environment()
|
||||
env_tests.set('XDG_DATA_DIRS', meson.current_build_dir() / 'share')
|
||||
env_tests.set('G_TEST_SRCDIR', meson.current_source_dir())
|
||||
env_tests.set('G_TEST_BUILDDIR', meson.current_build_dir())
|
||||
env_tests.set('G_DEBUG', 'gc-friendly') # from glib-tap.mk
|
||||
env_tests.set('LSAN_OPTIONS', 'suppressions=' + meson.current_source_dir() / 'asan.suppression')
|
||||
|
||||
if glib_dep.version().version_compare('>= 2.68')
|
||||
test_protocol = 'tap'
|
||||
else
|
||||
test_protocol = 'exitcode'
|
||||
endif
|
||||
|
||||
subdir('dbs')
|
||||
test_permission_db = executable(
|
||||
'test-permission-db',
|
||||
['test-permission-db.c'] + db_sources,
|
||||
dependencies: [common_deps],
|
||||
include_directories: [common_includes],
|
||||
install: enable_installed_tests,
|
||||
install_dir: installed_tests_dir,
|
||||
)
|
||||
test(
|
||||
'unit/permission-db',
|
||||
test_permission_db,
|
||||
suite: ['unit'],
|
||||
env: env_tests,
|
||||
is_parallel: false,
|
||||
protocol: test_protocol,
|
||||
)
|
||||
|
||||
test_xdp_utils = executable(
|
||||
'test-xdp-utils',
|
||||
'test-xdp-utils.c',
|
||||
xdp_utils_sources,
|
||||
dependencies: [common_deps, xdp_utils_deps],
|
||||
include_directories: [common_includes, xdp_utils_includes],
|
||||
install: enable_installed_tests,
|
||||
install_dir: installed_tests_dir,
|
||||
)
|
||||
test(
|
||||
'unit/xdp-utils',
|
||||
test_xdp_utils,
|
||||
suite: ['unit'],
|
||||
env: env_tests,
|
||||
is_parallel: false,
|
||||
protocol: test_protocol,
|
||||
)
|
||||
|
||||
test_method_info = executable(
|
||||
'test-xdp-method-info',
|
||||
'test-xdp-method-info.c',
|
||||
xdp_method_info_sources,
|
||||
dependencies: [common_deps],
|
||||
include_directories: [common_includes, xdp_utils_includes],
|
||||
install: enable_installed_tests,
|
||||
install_dir: installed_tests_dir,
|
||||
)
|
||||
test(
|
||||
'unit/xdp-method-info',
|
||||
test_method_info,
|
||||
suite: ['unit'],
|
||||
env: env_tests,
|
||||
is_parallel: true,
|
||||
protocol: test_protocol,
|
||||
)
|
||||
|
||||
run_test = find_program('run-test.sh')
|
||||
|
||||
pytest_args = ['--verbose', '--log-level=DEBUG']
|
||||
|
||||
pytest_env = environment()
|
||||
pytest_env.set('BUILDDIR', meson.project_build_root())
|
||||
|
||||
# pytest xdist is nice because it significantly speeds up our
|
||||
# test process, but it's not required
|
||||
if pymod.find_installation('python3', modules: ['xdist'], required: false).found()
|
||||
# using auto can easily start too many tests which will block each other
|
||||
# a value of around 5 seems to work well
|
||||
pytest_args += ['-n', '5']
|
||||
endif
|
||||
|
||||
pytest_files = [
|
||||
'test_account.py',
|
||||
'test_background.py',
|
||||
'test_camera.py',
|
||||
'test_clipboard.py',
|
||||
'test_documents.py',
|
||||
'test_document_fuse.py',
|
||||
'test_dynamiclauncher.py',
|
||||
'test_email.py',
|
||||
'test_filechooser.py',
|
||||
'test_globalshortcuts.py',
|
||||
'test_inhibit.py',
|
||||
'test_inputcapture.py',
|
||||
'test_location.py',
|
||||
'test_notification.py',
|
||||
'test_openuri.py',
|
||||
'test_permission_store.py',
|
||||
'test_print.py',
|
||||
'test_registry.py',
|
||||
'test_remotedesktop.py',
|
||||
'test_settings.py',
|
||||
'test_screenshot.py',
|
||||
'test_trash.py',
|
||||
'test_usb.py',
|
||||
'test_wallpaper.py',
|
||||
]
|
||||
|
||||
template_files = [
|
||||
'templates/access.py',
|
||||
'templates/account.py',
|
||||
'templates/appchooser.py',
|
||||
'templates/background.py',
|
||||
'templates/clipboard.py',
|
||||
'templates/dynamiclauncher.py',
|
||||
'templates/email.py',
|
||||
'templates/filechooser.py',
|
||||
'templates/geoclue2.py',
|
||||
'templates/globalshortcuts.py',
|
||||
'templates/inhibit.py',
|
||||
'templates/__init__.py',
|
||||
'templates/inputcapture.py',
|
||||
'templates/lockdown.py',
|
||||
'templates/notification.py',
|
||||
'templates/print.py',
|
||||
'templates/remotedesktop.py',
|
||||
'templates/screenshot.py',
|
||||
'templates/settings.py',
|
||||
'templates/usb.py',
|
||||
'templates/wallpaper.py',
|
||||
]
|
||||
|
||||
foreach pytest_file : pytest_files
|
||||
testname = pytest_file.replace('.py', '').replace('test_', '')
|
||||
test(
|
||||
'integration/@0@'.format(testname),
|
||||
run_test,
|
||||
args: [meson.current_source_dir() / pytest_file] + pytest_args,
|
||||
env: pytest_env,
|
||||
suite: ['integration'],
|
||||
timeout: 120,
|
||||
)
|
||||
endforeach
|
||||
|
||||
if enable_installed_tests
|
||||
install_data(
|
||||
pytest_files,
|
||||
'__init__.py',
|
||||
'conftest.py',
|
||||
'asan.suppression',
|
||||
install_dir: installed_tests_dir / 'tests',
|
||||
)
|
||||
install_data(
|
||||
template_files,
|
||||
install_dir: installed_tests_dir / 'tests' / 'templates',
|
||||
)
|
||||
|
||||
installed_env = {
|
||||
'XDG_DESKTOP_PORTAL_PATH': libexecdir / 'xdg-desktop-portal',
|
||||
'XDG_PERMISSION_STORE_PATH': libexecdir / 'xdg-permission-store',
|
||||
'XDG_DOCUMENT_PORTAL_PATH': libexecdir / 'xdg-document-portal',
|
||||
'XDP_VALIDATE_AUTO': '1',
|
||||
}
|
||||
env = ''
|
||||
foreach key, value : installed_env
|
||||
env += f'@key@=@value@ '
|
||||
endforeach
|
||||
|
||||
foreach pytest_file : pytest_files
|
||||
testname = pytest_file.replace('.py', '').replace('test_', '')
|
||||
|
||||
exec = [pytest.full_path(), installed_tests_dir / 'tests' / pytest_file]
|
||||
exec += pytest_args
|
||||
exec += ['-p', 'no:cacheprovider']
|
||||
exec = ' '.join(exec)
|
||||
|
||||
data = configuration_data()
|
||||
data.set('exec', exec)
|
||||
data.set('env', env)
|
||||
data.set('libdir', libdir)
|
||||
|
||||
configure_file(
|
||||
input: 'template.test.in',
|
||||
output: 'integration-@0@.test'.format(testname),
|
||||
configuration: data,
|
||||
install: true,
|
||||
install_dir: installed_tests_data_dir,
|
||||
)
|
||||
endforeach
|
||||
endif
|
||||
44
Telegram/ThirdParty/xdg-desktop-portal/tests/run-test.sh
vendored
Executable file
44
Telegram/ThirdParty/xdg-desktop-portal/tests/run-test.sh
vendored
Executable file
@@ -0,0 +1,44 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# - Runs pytest with the required environment to run tests on an x-d-p build
|
||||
# - By default, the tests run on the first x-d-p build directory that is found
|
||||
# inside the source tree
|
||||
# - The BUILDDIR environment variable can be set to a specific x-d-p build
|
||||
# directory
|
||||
# - All arguments are passed along to pytest
|
||||
# - Check tests/README.md for useful environment variables
|
||||
#
|
||||
# Examples:
|
||||
#
|
||||
# ./run-test.sh ./test_camera.py -k test_version -v -s
|
||||
#
|
||||
# BUILDDIR=../_build ./run-test.sh ./test_usb.py
|
||||
#
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
function fail()
|
||||
{
|
||||
sed -n '/^#$/,/^$/p' "${BASH_SOURCE[0]}"
|
||||
echo "$1"
|
||||
exit 1
|
||||
}
|
||||
|
||||
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
|
||||
|
||||
PYTEST=$(command -v "pytest-3" || command -v "pytest") || fail "pytest is missing"
|
||||
|
||||
BUILDDIR=${BUILDDIR:-$(find "${SCRIPT_DIR}/.." -maxdepth 2 -name "build.ninja" -printf "%h\n" -quit)}
|
||||
|
||||
[ ! -f "${BUILDDIR}/build.ninja" ] && fail "Path '${BUILDDIR}' does not appear to be a build dir"
|
||||
|
||||
echo "Running tests on build dir: $(readlink -f "${BUILDDIR}")"
|
||||
echo ""
|
||||
|
||||
export XDP_VALIDATE_SOUND="$BUILDDIR/src/xdg-desktop-portal-validate-sound"
|
||||
export XDP_VALIDATE_ICON="$BUILDDIR/src/xdg-desktop-portal-validate-icon"
|
||||
export XDG_DESKTOP_PORTAL_PATH="$BUILDDIR/src/xdg-desktop-portal"
|
||||
export XDG_DOCUMENT_PORTAL_PATH="$BUILDDIR/document-portal/xdg-document-portal"
|
||||
export XDG_PERMISSION_STORE_PATH="$BUILDDIR/document-portal/xdg-permission-store"
|
||||
|
||||
exec "$PYTEST" "$@"
|
||||
3
Telegram/ThirdParty/xdg-desktop-portal/tests/template.test.in
vendored
Normal file
3
Telegram/ThirdParty/xdg-desktop-portal/tests/template.test.in
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
[Test]
|
||||
Type=session
|
||||
Exec=env LSAN_OPTIONS=exitcode=0 LD_LIBRARY_PATH=@libdir@:$LD_LIBRARY_PATH @env@ @exec@
|
||||
286
Telegram/ThirdParty/xdg-desktop-portal/tests/templates/__init__.py
vendored
Normal file
286
Telegram/ThirdParty/xdg-desktop-portal/tests/templates/__init__.py
vendored
Normal 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}"
|
||||
74
Telegram/ThirdParty/xdg-desktop-portal/tests/templates/access.py
vendored
Normal file
74
Telegram/ThirdParty/xdg-desktop-portal/tests/templates/access.py
vendored
Normal 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)
|
||||
64
Telegram/ThirdParty/xdg-desktop-portal/tests/templates/account.py
vendored
Normal file
64
Telegram/ThirdParty/xdg-desktop-portal/tests/templates/account.py
vendored
Normal 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)
|
||||
84
Telegram/ThirdParty/xdg-desktop-portal/tests/templates/appchooser.py
vendored
Normal file
84
Telegram/ThirdParty/xdg-desktop-portal/tests/templates/appchooser.py
vendored
Normal 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})")
|
||||
76
Telegram/ThirdParty/xdg-desktop-portal/tests/templates/background.py
vendored
Normal file
76
Telegram/ThirdParty/xdg-desktop-portal/tests/templates/background.py
vendored
Normal 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")
|
||||
168
Telegram/ThirdParty/xdg-desktop-portal/tests/templates/clipboard.py
vendored
Normal file
168
Telegram/ThirdParty/xdg-desktop-portal/tests/templates/clipboard.py
vendored
Normal 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)
|
||||
85
Telegram/ThirdParty/xdg-desktop-portal/tests/templates/dynamiclauncher.py
vendored
Normal file
85
Telegram/ThirdParty/xdg-desktop-portal/tests/templates/dynamiclauncher.py
vendored
Normal 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)
|
||||
71
Telegram/ThirdParty/xdg-desktop-portal/tests/templates/email.py
vendored
Normal file
71
Telegram/ThirdParty/xdg-desktop-portal/tests/templates/email.py
vendored
Normal 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)
|
||||
98
Telegram/ThirdParty/xdg-desktop-portal/tests/templates/filechooser.py
vendored
Normal file
98
Telegram/ThirdParty/xdg-desktop-portal/tests/templates/filechooser.py
vendored
Normal 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)
|
||||
121
Telegram/ThirdParty/xdg-desktop-portal/tests/templates/geoclue2.py
vendored
Normal file
121
Telegram/ThirdParty/xdg-desktop-portal/tests/templates/geoclue2.py
vendored
Normal 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),
|
||||
],
|
||||
)
|
||||
165
Telegram/ThirdParty/xdg-desktop-portal/tests/templates/globalshortcuts.py
vendored
Normal file
165
Telegram/ThirdParty/xdg-desktop-portal/tests/templates/globalshortcuts.py
vendored
Normal 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, {}],
|
||||
)
|
||||
164
Telegram/ThirdParty/xdg-desktop-portal/tests/templates/inhibit.py
vendored
Normal file
164
Telegram/ThirdParty/xdg-desktop-portal/tests/templates/inhibit.py
vendored
Normal 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)
|
||||
336
Telegram/ThirdParty/xdg-desktop-portal/tests/templates/inputcapture.py
vendored
Normal file
336
Telegram/ThirdParty/xdg-desktop-portal/tests/templates/inputcapture.py
vendored
Normal 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
|
||||
48
Telegram/ThirdParty/xdg-desktop-portal/tests/templates/lockdown.py
vendored
Normal file
48
Telegram/ThirdParty/xdg-desktop-portal/tests/templates/lockdown.py
vendored
Normal 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)
|
||||
),
|
||||
}
|
||||
),
|
||||
)
|
||||
82
Telegram/ThirdParty/xdg-desktop-portal/tests/templates/notification.py
vendored
Normal file
82
Telegram/ThirdParty/xdg-desktop-portal/tests/templates/notification.py
vendored
Normal 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"),
|
||||
],
|
||||
)
|
||||
119
Telegram/ThirdParty/xdg-desktop-portal/tests/templates/print.py
vendored
Normal file
119
Telegram/ThirdParty/xdg-desktop-portal/tests/templates/print.py
vendored
Normal 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)
|
||||
185
Telegram/ThirdParty/xdg-desktop-portal/tests/templates/remotedesktop.py
vendored
Normal file
185
Telegram/ThirdParty/xdg-desktop-portal/tests/templates/remotedesktop.py
vendored
Normal 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
|
||||
98
Telegram/ThirdParty/xdg-desktop-portal/tests/templates/screenshot.py
vendored
Normal file
98
Telegram/ThirdParty/xdg-desktop-portal/tests/templates/screenshot.py
vendored
Normal 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)
|
||||
107
Telegram/ThirdParty/xdg-desktop-portal/tests/templates/settings.py
vendored
Normal file
107
Telegram/ThirdParty/xdg-desktop-portal/tests/templates/settings.py
vendored
Normal 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],
|
||||
)
|
||||
146
Telegram/ThirdParty/xdg-desktop-portal/tests/templates/usb.py
vendored
Normal file
146
Telegram/ThirdParty/xdg-desktop-portal/tests/templates/usb.py
vendored
Normal 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
|
||||
70
Telegram/ThirdParty/xdg-desktop-portal/tests/templates/wallpaper.py
vendored
Normal file
70
Telegram/ThirdParty/xdg-desktop-portal/tests/templates/wallpaper.py
vendored
Normal 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)
|
||||
360
Telegram/ThirdParty/xdg-desktop-portal/tests/test-permission-db.c
vendored
Normal file
360
Telegram/ThirdParty/xdg-desktop-portal/tests/test-permission-db.c
vendored
Normal file
@@ -0,0 +1,360 @@
|
||||
#include "config.h"
|
||||
|
||||
#include <glib.h>
|
||||
#include <document-portal/permission-db.h>
|
||||
|
||||
/*
|
||||
static void
|
||||
dump_db (PermissionDb *db)
|
||||
{
|
||||
g_autofree char *s = permission_db_print (db);
|
||||
g_printerr ("\n%s\n", s);
|
||||
}
|
||||
*/
|
||||
|
||||
static PermissionDb *
|
||||
create_test_db (gboolean serialized)
|
||||
{
|
||||
PermissionDb *db;
|
||||
|
||||
g_autoptr(PermissionDbEntry) entry1 = NULL;
|
||||
g_autoptr(PermissionDbEntry) entry2 = NULL;
|
||||
g_autoptr(PermissionDbEntry) entry3 = NULL;
|
||||
g_autoptr(PermissionDbEntry) entry4 = NULL;
|
||||
g_autoptr(PermissionDbEntry) entry5 = NULL;
|
||||
g_autoptr(PermissionDbEntry) entry6 = NULL;
|
||||
g_autoptr(PermissionDbEntry) entry7 = NULL;
|
||||
GError *error = NULL;
|
||||
const char *permissions1[] = { "read", "write", NULL };
|
||||
const char *permissions2[] = { "read", NULL };
|
||||
const char *permissions3[] = { "write", NULL };
|
||||
|
||||
db = permission_db_new (NULL, FALSE, &error);
|
||||
g_assert_no_error (error);
|
||||
g_assert (db != NULL);
|
||||
|
||||
{
|
||||
g_auto(GStrv) ids = permission_db_list_ids (db);
|
||||
g_assert (ids != NULL);
|
||||
g_assert (ids[0] == NULL);
|
||||
}
|
||||
|
||||
{
|
||||
g_auto(GStrv) apps = permission_db_list_apps (db);
|
||||
g_assert (apps != NULL);
|
||||
g_assert (apps[0] == NULL);
|
||||
}
|
||||
|
||||
entry1 = permission_db_entry_new (g_variant_new_string ("foo-data"));
|
||||
entry2 = permission_db_entry_set_app_permissions (entry1, "org.test.bapp", permissions2);
|
||||
entry3 = permission_db_entry_set_app_permissions (entry2, "org.test.app", permissions1);
|
||||
entry4 = permission_db_entry_set_app_permissions (entry3, "org.test.capp", permissions1);
|
||||
|
||||
permission_db_set_entry (db, "foo", entry4);
|
||||
|
||||
entry5 = permission_db_entry_new (g_variant_new_string ("bar-data"));
|
||||
entry6 = permission_db_entry_set_app_permissions (entry5, "org.test.app", permissions2);
|
||||
entry7 = permission_db_entry_set_app_permissions (entry6, "org.test.dapp", permissions3);
|
||||
|
||||
permission_db_set_entry (db, "bar", entry7);
|
||||
|
||||
if (serialized)
|
||||
permission_db_update (db);
|
||||
|
||||
return db;
|
||||
}
|
||||
|
||||
static void
|
||||
verify_test_db (PermissionDb *db)
|
||||
{
|
||||
g_auto(GStrv) ids = NULL;
|
||||
g_autofree const char **apps1 = NULL;
|
||||
g_autofree const char **apps2 = NULL;
|
||||
g_auto(GStrv) all_apps = NULL;
|
||||
|
||||
ids = permission_db_list_ids (db);
|
||||
g_assert (g_strv_length (ids) == 2);
|
||||
g_assert (g_strv_contains ((const char **) ids, "foo"));
|
||||
g_assert (g_strv_contains ((const char **) ids, "bar"));
|
||||
|
||||
{
|
||||
g_autoptr(PermissionDbEntry) entry = NULL;
|
||||
g_autofree const char **permissions1 = NULL;
|
||||
g_autofree const char **permissions2 = NULL;
|
||||
g_autofree const char **permissions3 = NULL;
|
||||
g_autofree const char **permissions4 = NULL;
|
||||
g_autoptr(GVariant) data1 = NULL;
|
||||
|
||||
entry = permission_db_lookup (db, "foo");
|
||||
g_assert (entry != NULL);
|
||||
data1 = permission_db_entry_get_data (entry);
|
||||
g_assert (data1 != NULL);
|
||||
g_assert_cmpstr (g_variant_get_type_string (data1), ==, "s");
|
||||
g_assert_cmpstr (g_variant_get_string (data1, NULL), ==, "foo-data");
|
||||
apps1 = permission_db_entry_list_apps (entry);
|
||||
g_assert (g_strv_length ((char **) apps1) == 3);
|
||||
g_assert (g_strv_contains (apps1, "org.test.app"));
|
||||
g_assert (g_strv_contains (apps1, "org.test.bapp"));
|
||||
g_assert (g_strv_contains (apps1, "org.test.capp"));
|
||||
permissions1 = permission_db_entry_list_permissions (entry, "org.test.app");
|
||||
g_assert (g_strv_length ((char **) permissions1) == 2);
|
||||
g_assert (g_strv_contains (permissions1, "read"));
|
||||
g_assert (g_strv_contains (permissions1, "write"));
|
||||
permissions2 = permission_db_entry_list_permissions (entry, "org.test.bapp");
|
||||
g_assert (g_strv_length ((char **) permissions2) == 1);
|
||||
g_assert (g_strv_contains (permissions2, "read"));
|
||||
permissions3 = permission_db_entry_list_permissions (entry, "org.test.capp");
|
||||
g_assert (g_strv_length ((char **) permissions3) == 2);
|
||||
g_assert (g_strv_contains (permissions3, "read"));
|
||||
g_assert (g_strv_contains (permissions3, "write"));
|
||||
permissions4 = permission_db_entry_list_permissions (entry, "org.test.noapp");
|
||||
g_assert (permissions4 != NULL);
|
||||
g_assert (g_strv_length ((char **) permissions4) == 0);
|
||||
}
|
||||
|
||||
{
|
||||
g_autoptr(PermissionDbEntry) entry = NULL;
|
||||
g_autofree const char **permissions5 = NULL;
|
||||
g_autofree const char **permissions6 = NULL;
|
||||
g_autoptr(GVariant) data2 = NULL;
|
||||
|
||||
entry = permission_db_lookup (db, "bar");
|
||||
g_assert (entry != NULL);
|
||||
data2 = permission_db_entry_get_data (entry);
|
||||
g_assert (data2 != NULL);
|
||||
g_assert_cmpstr (g_variant_get_type_string (data2), ==, "s");
|
||||
g_assert_cmpstr (g_variant_get_string (data2, NULL), ==, "bar-data");
|
||||
apps2 = permission_db_entry_list_apps (entry);
|
||||
g_assert (g_strv_length ((char **) apps2) == 2);
|
||||
g_assert (g_strv_contains (apps2, "org.test.app"));
|
||||
g_assert (g_strv_contains (apps2, "org.test.dapp"));
|
||||
permissions5 = permission_db_entry_list_permissions (entry, "org.test.app");
|
||||
g_assert (g_strv_length ((char **) permissions5) == 1);
|
||||
g_assert (g_strv_contains (permissions5, "read"));
|
||||
permissions6 = permission_db_entry_list_permissions (entry, "org.test.dapp");
|
||||
g_assert (g_strv_length ((char **) permissions6) == 1);
|
||||
g_assert (g_strv_contains (permissions6, "write"));
|
||||
}
|
||||
|
||||
{
|
||||
g_autoptr(PermissionDbEntry) entry = NULL;
|
||||
entry = permission_db_lookup (db, "gazonk");
|
||||
g_assert (entry == NULL);
|
||||
}
|
||||
|
||||
all_apps = permission_db_list_apps (db);
|
||||
g_assert (g_strv_length (all_apps) == 4);
|
||||
g_assert (g_strv_contains ((const char **) all_apps, "org.test.app"));
|
||||
g_assert (g_strv_contains ((const char **) all_apps, "org.test.bapp"));
|
||||
g_assert (g_strv_contains ((const char **) all_apps, "org.test.capp"));
|
||||
g_assert (g_strv_contains ((const char **) all_apps, "org.test.dapp"));
|
||||
}
|
||||
|
||||
static void
|
||||
test_db_open (void)
|
||||
{
|
||||
GError *error = NULL;
|
||||
PermissionDb *db;
|
||||
|
||||
db = permission_db_new (g_test_get_filename (G_TEST_DIST, "dbs", "does_not_exist", NULL), TRUE, &error);
|
||||
g_assert (g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT) ||
|
||||
g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND));
|
||||
g_assert (db == NULL);
|
||||
g_clear_error (&error);
|
||||
|
||||
db = permission_db_new (g_test_get_filename (G_TEST_DIST, "dbs", "does_not_exist", NULL), FALSE, &error);
|
||||
g_assert_no_error (error);
|
||||
g_assert (db != NULL);
|
||||
g_clear_error (&error);
|
||||
g_object_unref (db);
|
||||
|
||||
db = permission_db_new (g_test_get_filename (G_TEST_DIST, "dbs", "no_tables", NULL), TRUE, &error);
|
||||
g_assert_error (error, G_FILE_ERROR, G_FILE_ERROR_INVAL);
|
||||
g_assert (db == NULL);
|
||||
g_clear_error (&error);
|
||||
}
|
||||
|
||||
static void
|
||||
test_serialize (void)
|
||||
{
|
||||
g_autoptr(PermissionDb) db = NULL;
|
||||
g_autoptr(PermissionDb) db2 = NULL;
|
||||
g_autofree char *dump1 = NULL;
|
||||
g_autofree char *dump2 = NULL;
|
||||
g_autofree char *dump3 = NULL;
|
||||
GError *error = NULL;
|
||||
char tmpfile[] = "/tmp/test-permission-db-XXXXXX";
|
||||
int fd;
|
||||
|
||||
db = create_test_db (FALSE);
|
||||
|
||||
verify_test_db (db);
|
||||
|
||||
dump1 = permission_db_print (db);
|
||||
|
||||
g_assert (permission_db_is_dirty (db));
|
||||
|
||||
permission_db_update (db);
|
||||
|
||||
verify_test_db (db);
|
||||
|
||||
g_assert (!permission_db_is_dirty (db));
|
||||
|
||||
dump2 = permission_db_print (db);
|
||||
|
||||
g_assert_cmpstr (dump1, ==, dump2);
|
||||
|
||||
fd = g_mkstemp (tmpfile);
|
||||
close (fd);
|
||||
|
||||
permission_db_set_path (db, tmpfile);
|
||||
|
||||
permission_db_save_content (db, &error);
|
||||
g_assert_no_error (error);
|
||||
|
||||
db2 = permission_db_new (tmpfile, TRUE, &error);
|
||||
g_assert_no_error (error);
|
||||
g_assert (db2 != NULL);
|
||||
|
||||
dump3 = permission_db_print (db2);
|
||||
|
||||
g_assert_cmpstr (dump1, ==, dump3);
|
||||
|
||||
unlink (tmpfile);
|
||||
}
|
||||
|
||||
static void
|
||||
test_modify (void)
|
||||
{
|
||||
g_autoptr(PermissionDb) db = NULL;
|
||||
const char *permissions[] = { "read", "write", "execute", NULL };
|
||||
const char *no_permissions[] = { NULL };
|
||||
|
||||
db = create_test_db (FALSE);
|
||||
|
||||
/* Add permission */
|
||||
{
|
||||
g_autoptr(PermissionDbEntry) entry1 = NULL;
|
||||
g_autoptr(PermissionDbEntry) entry2 = NULL;
|
||||
|
||||
entry1 = permission_db_lookup (db, "foo");
|
||||
entry2 = permission_db_entry_set_app_permissions (entry1, "org.test.app", permissions);
|
||||
permission_db_set_entry (db, "foo", entry2);
|
||||
}
|
||||
|
||||
/* Add entry */
|
||||
{
|
||||
g_autoptr(PermissionDbEntry) entry1 = NULL;
|
||||
g_autoptr(PermissionDbEntry) entry2 = NULL;
|
||||
|
||||
entry1 = permission_db_entry_new (g_variant_new_string ("gazonk-data"));
|
||||
entry2 = permission_db_entry_set_app_permissions (entry1, "org.test.eapp", permissions);
|
||||
permission_db_set_entry (db, "gazonk", entry2);
|
||||
}
|
||||
|
||||
/* Remove permission */
|
||||
{
|
||||
g_autoptr(PermissionDbEntry) entry1 = NULL;
|
||||
g_autoptr(PermissionDbEntry) entry2 = NULL;
|
||||
|
||||
entry1 = permission_db_lookup (db, "bar");
|
||||
entry2 = permission_db_entry_set_app_permissions (entry1, "org.test.dapp", no_permissions);
|
||||
permission_db_set_entry (db, "bar", entry2);
|
||||
}
|
||||
|
||||
/* Verify */
|
||||
{
|
||||
g_autoptr(PermissionDbEntry) entry5 = NULL;
|
||||
g_autoptr(PermissionDbEntry) entry6 = NULL;
|
||||
g_autoptr(PermissionDbEntry) entry7 = NULL;
|
||||
g_autofree const char **apps2 = NULL;
|
||||
g_auto(GStrv) apps3 = NULL;
|
||||
g_autofree const char **permissions1 = NULL;
|
||||
g_autofree const char **permissions2 = NULL;
|
||||
g_autofree const char **permissions3 = NULL;
|
||||
|
||||
entry5 = permission_db_lookup (db, "foo");
|
||||
permissions1 = permission_db_entry_list_permissions (entry5, "org.test.app");
|
||||
g_assert (g_strv_length ((char **) permissions1) == 3);
|
||||
g_assert (g_strv_contains (permissions1, "read"));
|
||||
g_assert (g_strv_contains (permissions1, "write"));
|
||||
g_assert (g_strv_contains (permissions1, "execute"));
|
||||
|
||||
entry6 = permission_db_lookup (db, "bar");
|
||||
permissions2 = permission_db_entry_list_permissions (entry6, "org.test.dapp");
|
||||
g_assert (g_strv_length ((char **) permissions2) == 0);
|
||||
|
||||
entry7 = permission_db_lookup (db, "gazonk");
|
||||
permissions3 = permission_db_entry_list_permissions (entry7, "org.test.eapp");
|
||||
g_assert (g_strv_length ((char **) permissions3) == 3);
|
||||
g_assert (g_strv_contains (permissions3, "read"));
|
||||
g_assert (g_strv_contains (permissions3, "write"));
|
||||
g_assert (g_strv_contains (permissions3, "execute"));
|
||||
|
||||
apps2 = permission_db_entry_list_apps (entry6);
|
||||
g_assert_cmpint (g_strv_length ((char **) apps2), ==, 1);
|
||||
g_assert (g_strv_contains (apps2, "org.test.app"));
|
||||
|
||||
apps3 = permission_db_list_apps (db);
|
||||
g_assert_cmpint (g_strv_length (apps3), ==, 4);
|
||||
g_assert (g_strv_contains ((const char **) apps3, "org.test.app"));
|
||||
g_assert (g_strv_contains ((const char **) apps3, "org.test.bapp"));
|
||||
g_assert (g_strv_contains ((const char **) apps3, "org.test.capp"));
|
||||
g_assert (g_strv_contains ((const char **) apps3, "org.test.eapp"));
|
||||
}
|
||||
|
||||
permission_db_update (db);
|
||||
|
||||
/* Verify after serialize */
|
||||
{
|
||||
g_autoptr(PermissionDbEntry) entry5 = NULL;
|
||||
g_autoptr(PermissionDbEntry) entry6 = NULL;
|
||||
g_autoptr(PermissionDbEntry) entry7 = NULL;
|
||||
g_autofree const char **apps2 = NULL;
|
||||
g_auto(GStrv) apps3 = NULL;
|
||||
g_autofree const char **permissions1 = NULL;
|
||||
g_autofree const char **permissions2 = NULL;
|
||||
g_autofree const char **permissions3 = NULL;
|
||||
|
||||
entry5 = permission_db_lookup (db, "foo");
|
||||
permissions1 = permission_db_entry_list_permissions (entry5, "org.test.app");
|
||||
g_assert (g_strv_length ((char **) permissions1) == 3);
|
||||
g_assert (g_strv_contains (permissions1, "read"));
|
||||
g_assert (g_strv_contains (permissions1, "write"));
|
||||
g_assert (g_strv_contains (permissions1, "execute"));
|
||||
|
||||
entry6 = permission_db_lookup (db, "bar");
|
||||
permissions2 = permission_db_entry_list_permissions (entry6, "org.test.dapp");
|
||||
g_assert (g_strv_length ((char **) permissions2) == 0);
|
||||
|
||||
entry7 = permission_db_lookup (db, "gazonk");
|
||||
permissions3 = permission_db_entry_list_permissions (entry7, "org.test.eapp");
|
||||
g_assert (g_strv_length ((char **) permissions3) == 3);
|
||||
g_assert (g_strv_contains (permissions3, "read"));
|
||||
g_assert (g_strv_contains (permissions3, "write"));
|
||||
g_assert (g_strv_contains (permissions3, "execute"));
|
||||
|
||||
apps2 = permission_db_entry_list_apps (entry6);
|
||||
g_assert_cmpint (g_strv_length ((char **) apps2), ==, 1);
|
||||
g_assert (g_strv_contains (apps2, "org.test.app"));
|
||||
|
||||
apps3 = permission_db_list_apps (db);
|
||||
g_assert_cmpint (g_strv_length (apps3), ==, 4);
|
||||
g_assert (g_strv_contains ((const char **) apps3, "org.test.app"));
|
||||
g_assert (g_strv_contains ((const char **) apps3, "org.test.bapp"));
|
||||
g_assert (g_strv_contains ((const char **) apps3, "org.test.capp"));
|
||||
g_assert (g_strv_contains ((const char **) apps3, "org.test.eapp"));
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
g_test_init (&argc, &argv, NULL);
|
||||
|
||||
g_test_add_func ("/db/open", test_db_open);
|
||||
g_test_add_func ("/db/serialize", test_serialize);
|
||||
g_test_add_func ("/db/modify", test_modify);
|
||||
|
||||
return g_test_run ();
|
||||
}
|
||||
69
Telegram/ThirdParty/xdg-desktop-portal/tests/test-xdp-method-info.c
vendored
Normal file
69
Telegram/ThirdParty/xdg-desktop-portal/tests/test-xdp-method-info.c
vendored
Normal file
@@ -0,0 +1,69 @@
|
||||
#include "config.h"
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
#include "xdp-method-info.h"
|
||||
|
||||
static void
|
||||
test_method_info_all (void)
|
||||
{
|
||||
unsigned int i;
|
||||
unsigned int count = xdp_method_info_get_count ();
|
||||
const XdpMethodInfo *method_info = xdp_method_info_get_all ();
|
||||
|
||||
g_assert_cmpint (count, >, 100);
|
||||
g_assert_nonnull (method_info);
|
||||
|
||||
for (i = 0; i < count + 1; i++)
|
||||
{
|
||||
if (method_info->interface == NULL)
|
||||
return;
|
||||
g_assert_nonnull (method_info->method);
|
||||
method_info++;
|
||||
}
|
||||
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
static void
|
||||
test_method_info_find (void)
|
||||
{
|
||||
const XdpMethodInfo *method_info;
|
||||
|
||||
method_info = xdp_method_info_find ("org.freedesktop.portal.Notification", "AddNotification");
|
||||
g_assert_nonnull (method_info);
|
||||
g_assert_cmpint (method_info->option_arg, ==, -1);
|
||||
g_assert_false (method_info->uses_request);
|
||||
|
||||
method_info = xdp_method_info_find ("org.freedesktop.portal.Inhibit", "Inhibit");
|
||||
g_assert_nonnull (method_info);
|
||||
g_assert_cmpint (method_info->option_arg, ==, 2);
|
||||
g_assert_true (method_info->uses_request);
|
||||
|
||||
method_info = xdp_method_info_find ("org.freedesktop.portal.Inhibit", "QueryEndResponse");
|
||||
g_assert_nonnull (method_info);
|
||||
g_assert_cmpint (method_info->option_arg, ==, -1);
|
||||
g_assert_false (method_info->uses_request);
|
||||
|
||||
/* Prefix is required */
|
||||
method_info = xdp_method_info_find ("Inhibit", "QueryEndResponse");
|
||||
g_assert_null (method_info);
|
||||
|
||||
method_info = xdp_method_info_find ("DoesNotExist", "DoesNotExist");
|
||||
g_assert_null (method_info);
|
||||
|
||||
method_info = xdp_method_info_find ("DoesNotExist", "DoesNotExist");
|
||||
g_assert_null (method_info);
|
||||
|
||||
method_info = xdp_method_info_find ("org.freedesktop.portal.Inhibit", "DoesNotExist");
|
||||
g_assert_null (method_info);
|
||||
|
||||
}
|
||||
|
||||
int main (int argc, char **argv)
|
||||
{
|
||||
g_test_init (&argc, &argv, NULL);
|
||||
g_test_add_func ("/method-info/all", test_method_info_all);
|
||||
g_test_add_func ("/method-info/find", test_method_info_find);
|
||||
return g_test_run ();
|
||||
}
|
||||
205
Telegram/ThirdParty/xdg-desktop-portal/tests/test-xdp-utils.c
vendored
Normal file
205
Telegram/ThirdParty/xdg-desktop-portal/tests/test-xdp-utils.c
vendored
Normal file
@@ -0,0 +1,205 @@
|
||||
#include "config.h"
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
#include "xdp-app-info-private.h"
|
||||
#include "xdp-app-info-snap-private.h"
|
||||
#include "xdp-app-info-host-private.h"
|
||||
#include "xdp-utils.h"
|
||||
|
||||
#define snap_parse_cgroup _xdp_app_info_snap_parse_cgroup_file
|
||||
#define host_parse_app_id _xdp_app_info_host_parse_app_id_from_unit_name
|
||||
|
||||
static void
|
||||
test_parse_cgroup_unified (void)
|
||||
{
|
||||
char data[] = "0::/user.slice/user-1000.slice/user@1000.service/apps.slice/snap.something.scope\n";
|
||||
FILE *f;
|
||||
int res;
|
||||
gboolean is_snap = FALSE;
|
||||
|
||||
f = fmemopen(data, sizeof(data), "r");
|
||||
|
||||
res = snap_parse_cgroup (f, &is_snap);
|
||||
g_assert_cmpint (res, ==, 0);
|
||||
g_assert_true (is_snap);
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
static void
|
||||
test_parse_cgroup_freezer (void)
|
||||
{
|
||||
char data[] =
|
||||
"12:pids:/user.slice/user-1000.slice/user@1000.service\n"
|
||||
"11:perf_event:/\n"
|
||||
"10:net_cls,net_prio:/\n"
|
||||
"9:cpuset:/\n"
|
||||
"8:memory:/user.slice/user-1000.slice/user@1000.service/apps.slice/apps-org.gnome.Terminal.slice/vte-spawn-228ae109-a869-4533-8988-65ea4c10b492.scope\n"
|
||||
"7:rdma:/\n"
|
||||
"6:devices:/user.slice\n"
|
||||
"5:blkio:/user.slice\n"
|
||||
"4:hugetlb:/\n"
|
||||
"3:freezer:/snap.portal-test\n"
|
||||
"2:cpu,cpuacct:/user.slice\n"
|
||||
"1:name=systemd:/user.slice/user-1000.slice/user@1000.service/apps.slice/apps-org.gnome.Terminal.slice/vte-spawn-228ae109-a869-4533-8988-65ea4c10b492.scope\n"
|
||||
"0::/user.slice/user-1000.slice/user@1000.service/apps.slice/apps-org.gnome.Terminal.slice/vte-spawn-228ae109-a869-4533-8988-65ea4c10b492.scope\n";
|
||||
FILE *f;
|
||||
int res;
|
||||
gboolean is_snap = FALSE;
|
||||
|
||||
f = fmemopen(data, sizeof(data), "r");
|
||||
|
||||
res = snap_parse_cgroup (f, &is_snap);
|
||||
g_assert_cmpint (res, ==, 0);
|
||||
g_assert_true (is_snap);
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
static void
|
||||
test_parse_cgroup_systemd (void)
|
||||
{
|
||||
char data[] = "1:name=systemd:/user.slice/user-1000.slice/user@1000.service/apps.slice/snap.something.scope\n";
|
||||
FILE *f;
|
||||
int res;
|
||||
gboolean is_snap = FALSE;
|
||||
|
||||
f = fmemopen(data, sizeof(data), "r");
|
||||
|
||||
res = snap_parse_cgroup (f, &is_snap);
|
||||
g_assert_cmpint (res, ==, 0);
|
||||
g_assert_true (is_snap);
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
static void
|
||||
test_parse_cgroup_not_snap (void)
|
||||
{
|
||||
char data[] =
|
||||
"12:pids:/\n"
|
||||
"11:perf_event:/\n"
|
||||
"10:net_cls,net_prio:/\n"
|
||||
"9:cpuset:/\n"
|
||||
"8:memory:/\n"
|
||||
"7:rdma:/\n"
|
||||
"6:devices:/\n"
|
||||
"5:blkio:/\n"
|
||||
"4:hugetlb:/\n"
|
||||
"3:freezer:/\n"
|
||||
"2:cpu,cpuacct:/\n"
|
||||
"1:name=systemd:/\n"
|
||||
"0::/\n";
|
||||
|
||||
FILE *f;
|
||||
int res;
|
||||
gboolean is_snap = FALSE;
|
||||
|
||||
f = fmemopen(data, sizeof(data), "r");
|
||||
|
||||
res = snap_parse_cgroup (f, &is_snap);
|
||||
g_assert_cmpint (res, ==, 0);
|
||||
g_assert_false (is_snap);
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
static void
|
||||
test_alternate_doc_path (void)
|
||||
{
|
||||
g_autofree char *path = NULL;
|
||||
|
||||
xdp_set_documents_mountpoint (NULL);
|
||||
|
||||
/* If no documents mount point is set, there is no alternate path */
|
||||
path = xdp_get_alternate_document_path ("/whatever", "app-id");
|
||||
g_assert_cmpstr (path, ==, NULL);
|
||||
|
||||
xdp_set_documents_mountpoint ("/doc/portal");
|
||||
|
||||
/* Paths outside of the document portal do not have an alternate path */
|
||||
path = xdp_get_alternate_document_path ("/whatever", "app-id");
|
||||
g_assert_cmpstr (path, ==, NULL);
|
||||
|
||||
/* The doc portal mount point itself does not have an alternate path */
|
||||
path = xdp_get_alternate_document_path ("/doc/portal", "app-id");
|
||||
g_assert_cmpstr (path, ==, NULL);
|
||||
|
||||
/* Paths under the doc portal mount point have an alternate path */
|
||||
path = xdp_get_alternate_document_path ("/doc/portal/foo/bar", "app-id");
|
||||
g_assert_cmpstr (path, ==, "/doc/portal/by-app/app-id/foo/bar");
|
||||
|
||||
g_clear_pointer (&path, g_free);
|
||||
path = xdp_get_alternate_document_path ("/doc/portal/foo/bar", "second-app");
|
||||
g_assert_cmpstr (path, ==, "/doc/portal/by-app/second-app/foo/bar");
|
||||
|
||||
xdp_set_documents_mountpoint (NULL);
|
||||
}
|
||||
|
||||
#ifdef HAVE_LIBSYSTEMD
|
||||
static void
|
||||
test_app_id_via_systemd_unit (void)
|
||||
{
|
||||
g_autofree char *app_id = NULL;
|
||||
|
||||
app_id = host_parse_app_id ("app-not-a-well-formed-unit-name");
|
||||
g_assert_cmpstr (app_id, ==, "");
|
||||
g_clear_pointer (&app_id, g_free);
|
||||
|
||||
app_id = host_parse_app_id ("app-gnome-org.gnome.Evolution\\x2dalarm\\x2dnotify-2437.scope");
|
||||
/* Note, this is not Evolution's app ID, because the scope is for a background service */
|
||||
g_assert_cmpstr (app_id, ==, "org.gnome.Evolution-alarm-notify");
|
||||
g_clear_pointer (&app_id, g_free);
|
||||
|
||||
app_id = host_parse_app_id ("app-gnome-org.gnome.Epiphany-182352.scope");
|
||||
g_assert_cmpstr (app_id, ==, "org.gnome.Epiphany");
|
||||
g_clear_pointer (&app_id, g_free);
|
||||
|
||||
app_id = host_parse_app_id ("app-glib-spice\\x2dvdagent-1839.scope");
|
||||
g_assert_cmpstr (app_id, ==, "spice-vdagent");
|
||||
g_clear_pointer (&app_id, g_free);
|
||||
|
||||
app_id = host_parse_app_id ("app-KDE-org.kde.okular@12345.service");
|
||||
g_assert_cmpstr (app_id, ==, "org.kde.okular");
|
||||
g_clear_pointer (&app_id, g_free);
|
||||
|
||||
app_id = host_parse_app_id ("app-firefox.service");
|
||||
g_assert_cmpstr (app_id, ==, "firefox");
|
||||
g_clear_pointer (&app_id, g_free);
|
||||
|
||||
app_id = host_parse_app_id ("app-org.kde.amarok.service");
|
||||
g_assert_cmpstr (app_id, ==, "org.kde.amarok");
|
||||
g_clear_pointer (&app_id, g_free);
|
||||
|
||||
app_id = host_parse_app_id ("app-gnome-org.gnome.SettingsDaemon.DiskUtilityNotify-autostart.service");
|
||||
g_assert_cmpstr (app_id, ==, "org.gnome.SettingsDaemon.DiskUtilityNotify");
|
||||
g_clear_pointer (&app_id, g_free);
|
||||
|
||||
app_id = host_parse_app_id ("app-gnome-org.gnome.Terminal-92502.slice");
|
||||
g_assert_cmpstr (app_id, ==, "org.gnome.Terminal");
|
||||
g_clear_pointer (&app_id, g_free);
|
||||
|
||||
app_id = host_parse_app_id ("app-com.obsproject.Studio-d70acc38b5154a3a8b4a60accc4b15f4.scope");
|
||||
g_assert_cmpstr (app_id, ==, "com.obsproject.Studio");
|
||||
g_clear_pointer (&app_id, g_free);
|
||||
|
||||
app_id = host_parse_app_id ("app-firefox-jcfppqx.scope");
|
||||
g_assert_cmpstr (app_id, ==, "firefox");
|
||||
g_clear_pointer (&app_id, g_free);
|
||||
|
||||
app_id = host_parse_app_id ("app-gnome-firefox.service");
|
||||
g_assert_cmpstr (app_id, ==, "firefox");
|
||||
g_clear_pointer (&app_id, g_free);
|
||||
}
|
||||
#endif /* HAVE_LIBSYSTEMD */
|
||||
|
||||
int main (int argc, char **argv)
|
||||
{
|
||||
g_test_init (&argc, &argv, NULL);
|
||||
g_test_add_func ("/parse-cgroup/unified", test_parse_cgroup_unified);
|
||||
g_test_add_func ("/parse-cgroup/freezer", test_parse_cgroup_freezer);
|
||||
g_test_add_func ("/parse-cgroup/systemd", test_parse_cgroup_systemd);
|
||||
g_test_add_func ("/parse-cgroup/not-snap", test_parse_cgroup_not_snap);
|
||||
g_test_add_func ("/alternate-doc-path", test_alternate_doc_path);
|
||||
#ifdef HAVE_LIBSYSTEMD
|
||||
g_test_add_func ("/app-id-via-systemd-unit", test_app_id_via_systemd_unit);
|
||||
#endif
|
||||
return g_test_run ();
|
||||
}
|
||||
120
Telegram/ThirdParty/xdg-desktop-portal/tests/test_account.py
vendored
Normal file
120
Telegram/ThirdParty/xdg-desktop-portal/tests/test_account.py
vendored
Normal file
@@ -0,0 +1,120 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
#
|
||||
# This file is formatted with Python Black
|
||||
|
||||
import tests as xdp
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
ACCOUNT_DATA = {
|
||||
"id": "test",
|
||||
"name": "Test Name",
|
||||
"image": "file:///image.png",
|
||||
}
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def required_templates():
|
||||
return {
|
||||
"account": {
|
||||
"results": ACCOUNT_DATA,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
class TestAccount:
|
||||
def set_permission(self, dbus_con, app_id, permission):
|
||||
perm_store_intf = xdp.get_permission_store_iface(dbus_con)
|
||||
perm_store_intf.SetPermission(
|
||||
"wallpaper",
|
||||
True,
|
||||
"wallpaper",
|
||||
app_id,
|
||||
[permission],
|
||||
)
|
||||
|
||||
def test_version(self, portals, dbus_con):
|
||||
xdp.check_version(dbus_con, "Account", 1)
|
||||
|
||||
def test_basic1(self, portals, dbus_con, app_id):
|
||||
account_intf = xdp.get_portal_iface(dbus_con, "Account")
|
||||
mock_intf = xdp.get_mock_iface(dbus_con)
|
||||
|
||||
reason = "reason"
|
||||
|
||||
request = xdp.Request(dbus_con, account_intf)
|
||||
options = {
|
||||
"reason": reason,
|
||||
}
|
||||
response = request.call(
|
||||
"GetUserInformation",
|
||||
window="",
|
||||
options=options,
|
||||
)
|
||||
|
||||
assert response
|
||||
assert response.response == 0
|
||||
assert response.results["id"] == ACCOUNT_DATA["id"]
|
||||
assert response.results["name"] == ACCOUNT_DATA["name"]
|
||||
assert response.results["image"] == ACCOUNT_DATA["image"]
|
||||
|
||||
# Check the impl portal was called with the right args
|
||||
method_calls = mock_intf.GetMethodCalls("GetUserInformation")
|
||||
assert len(method_calls) > 0
|
||||
_, args = method_calls[-1]
|
||||
assert args[1] == app_id
|
||||
assert args[2] == "" # window
|
||||
assert args[3]["reason"] == reason
|
||||
|
||||
def test_reason(self, portals, dbus_con):
|
||||
account_intf = xdp.get_portal_iface(dbus_con, "Account")
|
||||
mock_intf = xdp.get_mock_iface(dbus_con)
|
||||
|
||||
reason = """This reason is unreasonably long, it stretches over
|
||||
more than twohundredfiftysix characters, which is really quite
|
||||
long. Excessively so. The portal frontend will silently drop
|
||||
reasons of this magnitude. If you can't express your reasons
|
||||
concisely, you probably have no good reason in the first place
|
||||
and are just waffling around."""
|
||||
|
||||
assert len(reason) - 1 > 256
|
||||
|
||||
request = xdp.Request(dbus_con, account_intf)
|
||||
options = {
|
||||
"reason": reason,
|
||||
}
|
||||
response = request.call(
|
||||
"GetUserInformation",
|
||||
window="",
|
||||
options=options,
|
||||
)
|
||||
|
||||
assert response
|
||||
assert response.response == 0
|
||||
|
||||
# Check the impl portal was called with the right args
|
||||
method_calls = mock_intf.GetMethodCalls("GetUserInformation")
|
||||
assert len(method_calls) > 0
|
||||
_, args = method_calls[-1]
|
||||
assert "reason" not in args[3]
|
||||
|
||||
@pytest.mark.parametrize("template_params", ({"account": {"expect-close": True}},))
|
||||
def test_close(self, portals, dbus_con):
|
||||
account_intf = xdp.get_portal_iface(dbus_con, "Account")
|
||||
|
||||
reason = "reason"
|
||||
|
||||
request = xdp.Request(dbus_con, account_intf)
|
||||
request.schedule_close(1000)
|
||||
options = {
|
||||
"reason": reason,
|
||||
}
|
||||
request.call(
|
||||
"GetUserInformation",
|
||||
window="",
|
||||
options=options,
|
||||
)
|
||||
|
||||
# Only true if the impl.Request was closed too
|
||||
assert request.closed
|
||||
167
Telegram/ThirdParty/xdg-desktop-portal/tests/test_background.py
vendored
Normal file
167
Telegram/ThirdParty/xdg-desktop-portal/tests/test_background.py
vendored
Normal file
@@ -0,0 +1,167 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
#
|
||||
# This file is formatted with Python Black
|
||||
|
||||
import tests as xdp
|
||||
|
||||
import dbus
|
||||
import pytest
|
||||
import os
|
||||
from pathlib import Path
|
||||
from gi.repository import GLib
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def required_templates():
|
||||
return {"background": {}}
|
||||
|
||||
|
||||
class TestBackground:
|
||||
def get_autostart_path(self, app_id):
|
||||
return Path(os.environ["XDG_CONFIG_HOME"]) / "autostart" / f"{app_id}.desktop"
|
||||
|
||||
def get_autostart_keyfile(self, app_id):
|
||||
keyfile = GLib.KeyFile.new()
|
||||
|
||||
desktop_file_path = self.get_autostart_path(app_id)
|
||||
with open(str(desktop_file_path.absolute())) as desktop_file:
|
||||
desktop_file_contents = desktop_file.read()
|
||||
|
||||
assert keyfile.load_from_data(
|
||||
desktop_file_contents,
|
||||
len(desktop_file_contents),
|
||||
GLib.KeyFileFlags.NONE,
|
||||
)
|
||||
|
||||
return keyfile
|
||||
|
||||
def test_version(self, portals, dbus_con):
|
||||
xdp.check_version(dbus_con, "Background", 2)
|
||||
|
||||
def test_request_background(self, portals, dbus_con, app_id):
|
||||
background_intf = xdp.get_portal_iface(dbus_con, "Background")
|
||||
desktop_file = self.get_autostart_path(app_id)
|
||||
|
||||
reason = "Testing portals"
|
||||
|
||||
request = xdp.Request(dbus_con, background_intf)
|
||||
options = {
|
||||
"reason": reason,
|
||||
}
|
||||
response = request.call(
|
||||
"RequestBackground",
|
||||
parent_window="",
|
||||
options=options,
|
||||
)
|
||||
|
||||
assert response
|
||||
assert response.response == 0
|
||||
assert response.results["background"]
|
||||
assert not response.results["autostart"]
|
||||
|
||||
assert not desktop_file.exists()
|
||||
|
||||
def test_autostart_desktopfile(self, portals, dbus_con, app_id):
|
||||
background_intf = xdp.get_portal_iface(dbus_con, "Background")
|
||||
|
||||
reason = "Testing portals"
|
||||
autostart = True
|
||||
commandline = ["/bin/true", "test"]
|
||||
dbus_activatable = True
|
||||
|
||||
request = xdp.Request(dbus_con, background_intf)
|
||||
options = {
|
||||
"reason": reason,
|
||||
"autostart": autostart,
|
||||
"commandline": commandline,
|
||||
"dbus-activatable": dbus_activatable,
|
||||
}
|
||||
response = request.call(
|
||||
"RequestBackground",
|
||||
parent_window="",
|
||||
options=options,
|
||||
)
|
||||
|
||||
assert response
|
||||
assert response.response == 0
|
||||
assert response.results["background"]
|
||||
assert response.results["autostart"]
|
||||
|
||||
keyfile = self.get_autostart_keyfile(app_id)
|
||||
assert keyfile.get_string("Desktop Entry", "Type") == "Application"
|
||||
assert keyfile.get_string("Desktop Entry", "Name") == app_id
|
||||
assert keyfile.get_string("Desktop Entry", "X-XDP-Autostart") == app_id
|
||||
assert keyfile.get_string("Desktop Entry", "Exec") == "/bin/true test"
|
||||
assert keyfile.get_boolean("Desktop Entry", "DBusActivatable")
|
||||
|
||||
def test_autostart_disable(self, portals, dbus_con, app_id):
|
||||
background_intf = xdp.get_portal_iface(dbus_con, "Background")
|
||||
desktop_file = self.get_autostart_path(app_id)
|
||||
|
||||
reason = "Testing portals"
|
||||
autostart = True
|
||||
|
||||
request = xdp.Request(dbus_con, background_intf)
|
||||
options = {
|
||||
"reason": reason,
|
||||
"autostart": autostart,
|
||||
}
|
||||
response = request.call(
|
||||
"RequestBackground",
|
||||
parent_window="",
|
||||
options=options,
|
||||
)
|
||||
|
||||
assert response
|
||||
assert response.response == 0
|
||||
assert response.results["background"]
|
||||
assert response.results["autostart"]
|
||||
|
||||
assert desktop_file.exists()
|
||||
|
||||
request = xdp.Request(dbus_con, background_intf)
|
||||
options = {
|
||||
"reason": reason,
|
||||
}
|
||||
response = request.call(
|
||||
"RequestBackground",
|
||||
parent_window="",
|
||||
options=options,
|
||||
)
|
||||
|
||||
assert response
|
||||
assert response.response == 0
|
||||
assert response.results["background"]
|
||||
assert not response.results["autostart"]
|
||||
|
||||
assert not desktop_file.exists()
|
||||
|
||||
def test_long_reason(self, portals, dbus_con, app_id):
|
||||
background_intf = xdp.get_portal_iface(dbus_con, "Background")
|
||||
|
||||
reason = (
|
||||
"012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"
|
||||
+ "012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"
|
||||
+ "012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"
|
||||
)
|
||||
autostart = True
|
||||
commandline = ["/bin/true", "test"]
|
||||
dbus_activatable = True
|
||||
|
||||
request = xdp.Request(dbus_con, background_intf)
|
||||
options = {
|
||||
"reason": reason,
|
||||
"autostart": autostart,
|
||||
"commandline": commandline,
|
||||
"dbus-activatable": dbus_activatable,
|
||||
}
|
||||
with pytest.raises(dbus.exceptions.DBusException) as excinfo:
|
||||
request.call(
|
||||
"RequestBackground",
|
||||
parent_window="",
|
||||
options=options,
|
||||
)
|
||||
assert (
|
||||
excinfo.value.get_dbus_name()
|
||||
== "org.freedesktop.portal.Error.InvalidArgument"
|
||||
)
|
||||
139
Telegram/ThirdParty/xdg-desktop-portal/tests/test_camera.py
vendored
Normal file
139
Telegram/ThirdParty/xdg-desktop-portal/tests/test_camera.py
vendored
Normal file
@@ -0,0 +1,139 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
#
|
||||
# This file is formatted with Python Black
|
||||
|
||||
import tests as xdp
|
||||
|
||||
import dbus
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def required_templates():
|
||||
return {
|
||||
"access": {},
|
||||
"lockdown": {},
|
||||
}
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def app_id():
|
||||
# x-d-p currently defaults to the empty app id for the camera portal for
|
||||
# host XdpAppInfos (which the XdpAppInfoTest is). So use the empty app_id
|
||||
# for now.
|
||||
return ""
|
||||
|
||||
|
||||
class TestCamera:
|
||||
def set_permissions(self, dbus_con, appid, permissions):
|
||||
perm_store_intf = xdp.get_permission_store_iface(dbus_con)
|
||||
perm_store_intf.SetPermission(
|
||||
"devices",
|
||||
True,
|
||||
"camera",
|
||||
appid,
|
||||
permissions,
|
||||
)
|
||||
|
||||
def test_version(self, portals, dbus_con):
|
||||
xdp.check_version(dbus_con, "Camera", 1)
|
||||
|
||||
def test_access(self, portals, dbus_con, app_id):
|
||||
camera_intf = xdp.get_portal_iface(dbus_con, "Camera")
|
||||
mock_intf = xdp.get_mock_iface(dbus_con)
|
||||
|
||||
request = xdp.Request(dbus_con, camera_intf)
|
||||
response = request.call(
|
||||
"AccessCamera",
|
||||
options={},
|
||||
)
|
||||
|
||||
assert response
|
||||
assert response.response == 0
|
||||
|
||||
# Check the impl portal was called with the right args
|
||||
method_calls = mock_intf.GetMethodCalls("AccessDialog")
|
||||
assert len(method_calls) == 1
|
||||
_, args = method_calls[-1]
|
||||
assert args[1] == app_id
|
||||
|
||||
@pytest.mark.parametrize("template_params", ({"access": {"response": 1}},))
|
||||
def test_access_cancel(self, portals, dbus_con, app_id):
|
||||
camera_intf = xdp.get_portal_iface(dbus_con, "Camera")
|
||||
mock_intf = xdp.get_mock_iface(dbus_con)
|
||||
|
||||
request = xdp.Request(dbus_con, camera_intf)
|
||||
response = request.call(
|
||||
"AccessCamera",
|
||||
options={},
|
||||
)
|
||||
|
||||
assert response
|
||||
assert response.response == 1
|
||||
|
||||
# Check the impl portal was called with the right args
|
||||
method_calls = mock_intf.GetMethodCalls("AccessDialog")
|
||||
assert len(method_calls) == 1
|
||||
_, args = method_calls[-1]
|
||||
assert args[1] == app_id
|
||||
|
||||
@pytest.mark.parametrize("template_params", ({"access": {"expect-close": True}},))
|
||||
def test_access_close(self, portals, dbus_con, app_id):
|
||||
camera_intf = xdp.get_portal_iface(dbus_con, "Camera")
|
||||
mock_intf = xdp.get_mock_iface(dbus_con)
|
||||
|
||||
request = xdp.Request(dbus_con, camera_intf)
|
||||
request.schedule_close(1000)
|
||||
request.call(
|
||||
"AccessCamera",
|
||||
options={},
|
||||
)
|
||||
|
||||
# Only true if the impl.Request was closed too
|
||||
assert request.closed
|
||||
|
||||
# Check the impl portal was called with the right args
|
||||
method_calls = mock_intf.GetMethodCalls("AccessDialog")
|
||||
assert len(method_calls) == 1
|
||||
_, args = method_calls[-1]
|
||||
assert args[1] == app_id
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"template_params", ({"lockdown": {"disable-camera": True}},)
|
||||
)
|
||||
def test_access_lockdown(self, portals, dbus_con, app_id):
|
||||
camera_intf = xdp.get_portal_iface(dbus_con, "Camera")
|
||||
mock_intf = xdp.get_mock_iface(dbus_con)
|
||||
|
||||
request = xdp.Request(dbus_con, camera_intf)
|
||||
with pytest.raises(dbus.exceptions.DBusException) as excinfo:
|
||||
request.call(
|
||||
"AccessCamera",
|
||||
options={},
|
||||
)
|
||||
assert (
|
||||
excinfo.value.get_dbus_name() == "org.freedesktop.portal.Error.NotAllowed"
|
||||
)
|
||||
|
||||
# Check the impl portal was called with the right args
|
||||
method_calls = mock_intf.GetMethodCalls("AccessDialog")
|
||||
assert len(method_calls) == 0
|
||||
|
||||
def test_access_denied(self, portals, dbus_con, app_id):
|
||||
camera_intf = xdp.get_portal_iface(dbus_con, "Camera")
|
||||
mock_intf = xdp.get_mock_iface(dbus_con)
|
||||
|
||||
self.set_permissions(dbus_con, app_id, ["no"])
|
||||
|
||||
request = xdp.Request(dbus_con, camera_intf)
|
||||
response = request.call(
|
||||
"AccessCamera",
|
||||
options={},
|
||||
)
|
||||
|
||||
assert response
|
||||
assert response.response == 1
|
||||
|
||||
# Check the impl portal was called with the right args
|
||||
method_calls = mock_intf.GetMethodCalls("AccessDialog")
|
||||
assert len(method_calls) == 0
|
||||
103
Telegram/ThirdParty/xdg-desktop-portal/tests/test_clipboard.py
vendored
Normal file
103
Telegram/ThirdParty/xdg-desktop-portal/tests/test_clipboard.py
vendored
Normal file
@@ -0,0 +1,103 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
#
|
||||
# This file is formatted with Python Black
|
||||
|
||||
import tests as xdp
|
||||
|
||||
import dbus
|
||||
import pytest
|
||||
import os
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def required_templates():
|
||||
return {
|
||||
"Clipboard": {},
|
||||
"RemoteDesktop": {"force-clipboard-enabled": True},
|
||||
}
|
||||
|
||||
|
||||
class TestClipboard:
|
||||
def test_version(self, portals, dbus_con):
|
||||
xdp.check_version(dbus_con, "Clipboard", 1)
|
||||
|
||||
def start_session(self, dbus_con):
|
||||
clipboard_intf = xdp.get_portal_iface(dbus_con, "Clipboard")
|
||||
remotedesktop_intf = xdp.get_portal_iface(dbus_con, "RemoteDesktop")
|
||||
|
||||
create_session_request = xdp.Request(dbus_con, remotedesktop_intf)
|
||||
create_session_response = create_session_request.call(
|
||||
"CreateSession", options={"session_handle_token": "1234"}
|
||||
)
|
||||
assert create_session_response
|
||||
assert create_session_response.response == 0
|
||||
assert str(create_session_response.results["session_handle"])
|
||||
|
||||
session = xdp.Session.from_response(dbus_con, create_session_response)
|
||||
|
||||
clipboard_intf.RequestClipboard(session.handle, {})
|
||||
|
||||
start_session_request = xdp.Request(dbus_con, remotedesktop_intf)
|
||||
start_session_response = start_session_request.call(
|
||||
"Start", session_handle=session.handle, parent_window="", options={}
|
||||
)
|
||||
|
||||
assert start_session_response
|
||||
assert start_session_response.response == 0
|
||||
|
||||
return (session, start_session_response.results.get("clipboard_enabled"))
|
||||
|
||||
def test_request_clipboard_and_start_session(self, portals, dbus_con):
|
||||
_, clipboard_enabled = self.start_session(dbus_con)
|
||||
|
||||
assert clipboard_enabled
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"template_params", ({"RemoteDesktop": {"force-clipboard-enabled": False}},)
|
||||
)
|
||||
def test_checks_clipboard_enabled(self, portals, dbus_con):
|
||||
clipboard_intf = xdp.get_portal_iface(dbus_con, "Clipboard")
|
||||
session, clipboard_enabled = self.start_session(dbus_con)
|
||||
|
||||
assert not clipboard_enabled
|
||||
|
||||
with pytest.raises(dbus.exceptions.DBusException):
|
||||
clipboard_intf.SetSelection(session.handle, {})
|
||||
|
||||
def test_set_selection(self, portals, dbus_con):
|
||||
clipboard_intf = xdp.get_portal_iface(dbus_con, "Clipboard")
|
||||
session, _ = self.start_session(dbus_con)
|
||||
|
||||
clipboard_intf.SetSelection(session.handle, {})
|
||||
|
||||
def test_selection_write(self, portals, dbus_con):
|
||||
clipboard_intf = xdp.get_portal_iface(dbus_con, "Clipboard")
|
||||
session, _ = self.start_session(dbus_con)
|
||||
|
||||
fd_object: dbus.types.UnixFd = clipboard_intf.SelectionWrite(
|
||||
session.handle, 1234
|
||||
)
|
||||
assert fd_object
|
||||
|
||||
fd = fd_object.take()
|
||||
assert fd
|
||||
|
||||
bytes_written = os.write(fd, b"Clipboard")
|
||||
assert bytes_written > 0
|
||||
|
||||
clipboard_intf.SelectionWriteDone(session.handle, 1234, True)
|
||||
|
||||
def test_selection_read(self, portals, dbus_con):
|
||||
clipboard_intf = xdp.get_portal_iface(dbus_con, "Clipboard")
|
||||
session, _ = self.start_session(dbus_con)
|
||||
|
||||
fd_object: dbus.types.UnixFd = clipboard_intf.SelectionRead(
|
||||
session.handle, "mimetype"
|
||||
)
|
||||
assert fd_object
|
||||
|
||||
fd = fd_object.take()
|
||||
assert fd
|
||||
|
||||
clipboard_contents = os.read(fd, 1000)
|
||||
assert str(clipboard_contents)
|
||||
1259
Telegram/ThirdParty/xdg-desktop-portal/tests/test_document_fuse.py
vendored
Normal file
1259
Telegram/ThirdParty/xdg-desktop-portal/tests/test_document_fuse.py
vendored
Normal file
File diff suppressed because it is too large
Load Diff
421
Telegram/ThirdParty/xdg-desktop-portal/tests/test_documents.py
vendored
Normal file
421
Telegram/ThirdParty/xdg-desktop-portal/tests/test_documents.py
vendored
Normal file
@@ -0,0 +1,421 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
#
|
||||
# This file is formatted with Python Black
|
||||
|
||||
import tests as xdp
|
||||
|
||||
import pytest
|
||||
import dbus
|
||||
from pathlib import Path
|
||||
import os
|
||||
from gi.repository import GLib, Gio
|
||||
|
||||
|
||||
EXPORT_FILES_FLAG_EXPORT_DIR = 8
|
||||
|
||||
|
||||
def path_from_null_term_bytes(bytes):
|
||||
path_bytes, rest = bytes.split(b"\x00")
|
||||
assert rest == b""
|
||||
return Path(os.fsdecode(path_bytes))
|
||||
|
||||
|
||||
def get_mountpoint(documents_intf):
|
||||
mountpoint = documents_intf.GetMountPoint(byte_arrays=True)
|
||||
mountpoint = path_from_null_term_bytes(mountpoint)
|
||||
assert mountpoint.exists()
|
||||
return mountpoint
|
||||
|
||||
|
||||
def write_bytes_atomic(file_path, bytes):
|
||||
GLib.file_set_contents(file_path.absolute().as_posix(), bytes)
|
||||
|
||||
|
||||
def write_bytes_trunc(file_path, bytes):
|
||||
try:
|
||||
fd = os.open(
|
||||
file_path.absolute().as_posix(), os.O_RDWR | os.O_TRUNC | os.O_CREAT
|
||||
)
|
||||
os.write(fd, bytes)
|
||||
finally:
|
||||
os.close(fd)
|
||||
|
||||
|
||||
def get_host_path_attr(path):
|
||||
xattr = "xattr::document-portal.host-path"
|
||||
file = Gio.file_new_for_path(path.absolute().as_posix())
|
||||
info = file.query_info(xattr, Gio.FileQueryInfoFlags.NONE)
|
||||
host_path = info.get_attribute_as_string(xattr)
|
||||
if not host_path:
|
||||
return None
|
||||
return Path(os.fsdecode(host_path))
|
||||
|
||||
|
||||
def export_file(documents_intf, file_path, unique=False):
|
||||
assert file_path.exists()
|
||||
|
||||
with open(file_path.absolute().as_posix(), "r") as file:
|
||||
doc_id = documents_intf.Add(file.fileno(), not unique, False)
|
||||
assert doc_id
|
||||
|
||||
return doc_id
|
||||
|
||||
|
||||
def export_file_named(documents_intf, folder_path, name, unique=False):
|
||||
assert folder_path.exists()
|
||||
|
||||
# bytestring convention is zero terminated
|
||||
name_nt = os.fsencode(name) + b"\x00"
|
||||
|
||||
try:
|
||||
fd = os.open(folder_path.absolute().as_posix(), os.O_PATH | os.O_CLOEXEC)
|
||||
doc_id = documents_intf.AddNamed(fd, name_nt, not unique, False)
|
||||
assert doc_id
|
||||
finally:
|
||||
os.close(fd)
|
||||
|
||||
return doc_id
|
||||
|
||||
|
||||
def export_files(documents_intf, file_paths, perms, flags=0, app_id=""):
|
||||
fds = []
|
||||
try:
|
||||
for file_path in file_paths:
|
||||
fds.append(
|
||||
os.open(file_path.absolute().as_posix(), os.O_PATH | os.O_CLOEXEC)
|
||||
)
|
||||
|
||||
result = documents_intf.AddFull(
|
||||
fds,
|
||||
flags,
|
||||
app_id,
|
||||
perms,
|
||||
byte_arrays=True,
|
||||
)
|
||||
finally:
|
||||
for fd in fds:
|
||||
os.close(fd)
|
||||
|
||||
assert result
|
||||
return result
|
||||
|
||||
|
||||
class TestDocuments:
|
||||
def test_version(self, xdg_document_portal, dbus_con):
|
||||
documents = dbus_con.get_object(
|
||||
"org.freedesktop.portal.Documents",
|
||||
"/org/freedesktop/portal/documents",
|
||||
)
|
||||
|
||||
properties_intf = dbus.Interface(
|
||||
documents,
|
||||
"org.freedesktop.DBus.Properties",
|
||||
)
|
||||
portal_version = properties_intf.Get(
|
||||
"org.freedesktop.portal.Documents",
|
||||
"version",
|
||||
)
|
||||
assert int(portal_version) == 5
|
||||
|
||||
def test_mount_point(self, xdg_document_portal, dbus_con):
|
||||
documents_intf = xdp.get_document_portal_iface(dbus_con)
|
||||
get_mountpoint(documents_intf)
|
||||
|
||||
def test_create_doc(self, xdg_document_portal, dbus_con):
|
||||
documents_intf = xdp.get_document_portal_iface(dbus_con)
|
||||
mountpoint = get_mountpoint(documents_intf)
|
||||
|
||||
content = b"content"
|
||||
file_name = "a-file"
|
||||
|
||||
file_path = Path(os.environ["TMPDIR"]) / file_name
|
||||
write_bytes_atomic(file_path, content)
|
||||
doc_id = export_file(documents_intf, file_path)
|
||||
|
||||
doc_path = mountpoint / doc_id
|
||||
doc_app1_path = mountpoint / "by-app" / "com.test.App1" / doc_id
|
||||
doc_app2_path = mountpoint / "by-app" / "com.test.App2" / doc_id
|
||||
|
||||
# Make sure it got exported
|
||||
assert (doc_path / file_name).read_bytes() == content
|
||||
|
||||
assert not (doc_path / "another-file").exists()
|
||||
assert not (mountpoint / "anotherid" / file_name).exists()
|
||||
|
||||
# Make sure it is not viewable by apps
|
||||
assert not doc_app1_path.exists()
|
||||
assert not doc_app2_path.exists()
|
||||
|
||||
# Create a tmp file in same dir, ensure it works and can't be seen by other apps
|
||||
write_bytes_atomic(doc_path / "tmp1", b"tmpdata1")
|
||||
assert (doc_path / "tmp1").read_bytes() == b"tmpdata1"
|
||||
assert not (doc_app1_path / "tmp1").exists()
|
||||
|
||||
# Ensure App 1 and only it can see the document and tmpfile
|
||||
documents_intf.GrantPermissions(doc_id, "com.test.App1", ["read"])
|
||||
assert (doc_app1_path / file_name).read_bytes() == content
|
||||
assert not (doc_app2_path / file_name).exists()
|
||||
|
||||
# Make sure App 1 can't create a tmpfile
|
||||
assert not (doc_app1_path / "tmp2").exists()
|
||||
with pytest.raises(PermissionError):
|
||||
(doc_app1_path / "tmp2").write_bytes(b"tmpdata1")
|
||||
assert not (doc_app1_path / "tmp2").exists()
|
||||
assert not (doc_path / "tmp2").exists()
|
||||
|
||||
# Update the document contents, ensure this is propagated
|
||||
content = b"content2"
|
||||
write_bytes_atomic(doc_path / file_name, content)
|
||||
assert (doc_path / file_name).read_bytes() == content
|
||||
assert (doc_app1_path / file_name).read_bytes() == content
|
||||
assert file_path.read_bytes() == content
|
||||
assert not (doc_app2_path / file_name).exists()
|
||||
assert not (doc_app2_path / "tmp1").exists()
|
||||
|
||||
# Update the document contents outside fuse fd, ensure this is propagated
|
||||
content = b"content3"
|
||||
write_bytes_atomic(file_path, content)
|
||||
assert (doc_path / file_name).read_bytes() == content
|
||||
assert (doc_app1_path / file_name).read_bytes() == content
|
||||
assert file_path.read_bytes() == content
|
||||
assert not (doc_app2_path / file_name).exists()
|
||||
assert not (doc_app2_path / "tmp1").exists()
|
||||
|
||||
# Try to update the doc from an app that can't write to it
|
||||
with pytest.raises(PermissionError):
|
||||
(doc_app1_path / file_name).write_bytes(b"content4")
|
||||
|
||||
# Update the doc from an app with write access
|
||||
documents_intf.GrantPermissions(doc_id, "com.test.App1", ["write"])
|
||||
content = b"content5"
|
||||
write_bytes_atomic(doc_app1_path / file_name, content)
|
||||
assert (doc_path / file_name).read_bytes() == content
|
||||
assert (doc_app1_path / file_name).read_bytes() == content
|
||||
assert file_path.read_bytes() == content
|
||||
assert not (doc_app2_path / file_name).exists()
|
||||
|
||||
# Try to create a tmp file for an app
|
||||
assert not (doc_app1_path / "tmp3").exists()
|
||||
write_bytes_atomic(doc_app1_path / "tmp3", b"tmpdata2")
|
||||
(doc_app1_path / "tmp3").read_bytes() == b"tmpdata2"
|
||||
assert not (doc_path / "tmp3").exists()
|
||||
|
||||
# Re-Create a file from a fuse document file, in various ways
|
||||
doc_id2 = export_file(documents_intf, (doc_path / file_name))
|
||||
assert doc_id2 == doc_id
|
||||
doc_id3 = export_file(documents_intf, (doc_app1_path / file_name))
|
||||
assert doc_id3 == doc_id
|
||||
doc_id4 = export_file(documents_intf, file_path)
|
||||
assert doc_id4 == doc_id
|
||||
|
||||
# Ensure we can make a unique document
|
||||
doc_id5 = export_file(documents_intf, file_path, unique=True)
|
||||
assert doc_id5 != doc_id
|
||||
|
||||
def test_recursive_doc(self, xdg_document_portal, dbus_con):
|
||||
documents_intf = xdp.get_document_portal_iface(dbus_con)
|
||||
mountpoint = get_mountpoint(documents_intf)
|
||||
|
||||
content = b"content"
|
||||
file_name = "recursive-file"
|
||||
|
||||
file_path = Path(os.environ["TMPDIR"]) / file_name
|
||||
write_bytes_atomic(file_path, content)
|
||||
doc_id = export_file(documents_intf, file_path)
|
||||
|
||||
doc_path = mountpoint / doc_id
|
||||
doc_app1_path = mountpoint / "by-app" / "com.test.App1" / doc_id
|
||||
|
||||
assert (doc_path / file_name).read_bytes() == content
|
||||
|
||||
doc_id2 = export_file(documents_intf, doc_path / file_name)
|
||||
assert doc_id2 == doc_id
|
||||
|
||||
documents_intf.GrantPermissions(doc_id, "com.test.App1", ["read"])
|
||||
|
||||
doc_id3 = export_file(documents_intf, doc_app1_path / file_name)
|
||||
assert doc_id3 == doc_id
|
||||
|
||||
def test_create_docs(self, xdg_document_portal, dbus_con):
|
||||
documents_intf = xdp.get_document_portal_iface(dbus_con)
|
||||
mountpoint = get_mountpoint(documents_intf)
|
||||
|
||||
files = {
|
||||
"doc1": b"doc1-content",
|
||||
"doc2": b"doc2-content",
|
||||
}
|
||||
|
||||
file_paths = []
|
||||
for file_name, file_content in files.items():
|
||||
file_path = Path(os.environ["TMPDIR"]) / file_name
|
||||
write_bytes_atomic(file_path, file_content)
|
||||
file_paths.append(file_path)
|
||||
|
||||
doc_ids, extra = export_files(
|
||||
documents_intf, file_paths, ["read"], app_id="org.other.App"
|
||||
)
|
||||
|
||||
assert extra
|
||||
out_mountpoint = path_from_null_term_bytes(extra["mountpoint"])
|
||||
assert out_mountpoint == mountpoint
|
||||
|
||||
assert doc_ids
|
||||
for doc_id, (file_name, file_content) in zip(doc_ids, files.items()):
|
||||
assert (mountpoint / doc_id / file_name).read_bytes() == file_content
|
||||
assert (Path(os.environ["TMPDIR"]) / file_name).read_bytes() == file_content
|
||||
app1_path = mountpoint / "by-app" / "com.test.App1" / doc_id / file_name
|
||||
app2_path = mountpoint / "by-app" / "com.test.App2" / doc_id / file_name
|
||||
assert not app1_path.exists()
|
||||
assert not app2_path.exists()
|
||||
assert not (mountpoint / doc_id / "another-file").exists()
|
||||
assert not (mountpoint / "anotherid" / file_name).exists()
|
||||
|
||||
other_app_path = (
|
||||
mountpoint / "by-app" / "org.other.App" / doc_id / file_name
|
||||
)
|
||||
assert other_app_path.read_bytes() == file_content
|
||||
with pytest.raises(PermissionError):
|
||||
other_app_path.write_bytes(b"new-content")
|
||||
|
||||
def test_add_named(self, xdg_document_portal, dbus_con):
|
||||
documents_intf = xdp.get_document_portal_iface(dbus_con)
|
||||
mountpoint = get_mountpoint(documents_intf)
|
||||
|
||||
content = b"content"
|
||||
file_name = "add-named-1"
|
||||
|
||||
folder_path = Path(os.environ["TMPDIR"])
|
||||
doc_id = export_file_named(documents_intf, folder_path, file_name)
|
||||
assert doc_id
|
||||
|
||||
doc_path = mountpoint / doc_id
|
||||
doc_app1_path = mountpoint / "by-app" / "com.test.App1" / doc_id
|
||||
doc_app2_path = mountpoint / "by-app" / "com.test.App2" / doc_id
|
||||
|
||||
assert doc_path.exists()
|
||||
assert not doc_app1_path.exists()
|
||||
assert not (doc_path / file_name).exists()
|
||||
assert not (doc_app1_path / file_name).exists()
|
||||
|
||||
documents_intf.GrantPermissions(doc_id, "com.test.App1", ["read", "write"])
|
||||
|
||||
assert doc_path.exists()
|
||||
assert doc_app1_path.exists()
|
||||
assert not (doc_path / file_name).exists()
|
||||
assert not (doc_app1_path / file_name).exists()
|
||||
|
||||
# Update truncating with no previous file
|
||||
write_bytes_trunc(doc_path / file_name, content)
|
||||
assert (doc_path / file_name).read_bytes() == content
|
||||
assert (doc_app1_path / file_name).read_bytes() == content
|
||||
assert not (doc_app2_path / file_name).exists()
|
||||
|
||||
# Update truncating with previous file
|
||||
content = b"content2"
|
||||
write_bytes_trunc(doc_path / file_name, content)
|
||||
assert (doc_path / file_name).read_bytes() == content
|
||||
assert (doc_app1_path / file_name).read_bytes() == content
|
||||
assert not (doc_app2_path / file_name).exists()
|
||||
|
||||
# Update atomic with previous file
|
||||
content = b"content3"
|
||||
write_bytes_atomic(doc_path / file_name, content)
|
||||
assert (doc_path / file_name).read_bytes() == content
|
||||
assert (doc_app1_path / file_name).read_bytes() == content
|
||||
assert not (doc_app2_path / file_name).exists()
|
||||
|
||||
# Update from host
|
||||
content = b"content4"
|
||||
write_bytes_atomic(folder_path / file_name, content)
|
||||
assert (doc_path / file_name).read_bytes() == content
|
||||
assert (doc_app1_path / file_name).read_bytes() == content
|
||||
assert not (doc_app2_path / file_name).exists()
|
||||
|
||||
# Unlink doc
|
||||
(doc_path / file_name).unlink()
|
||||
assert doc_path.exists()
|
||||
assert doc_app1_path.exists()
|
||||
assert not (doc_path / file_name).exists()
|
||||
assert not (doc_app1_path / file_name).exists()
|
||||
|
||||
# Update atomic with no previous file
|
||||
content = b"content5"
|
||||
write_bytes_atomic(doc_path / file_name, content)
|
||||
assert (doc_path / file_name).read_bytes() == content
|
||||
assert (doc_app1_path / file_name).read_bytes() == content
|
||||
assert not (doc_app2_path / file_name).exists()
|
||||
|
||||
# Unlink doc on host
|
||||
(folder_path / file_name).unlink()
|
||||
assert doc_path.exists()
|
||||
assert doc_app1_path.exists()
|
||||
assert not (doc_path / file_name).exists()
|
||||
assert not (doc_app1_path / file_name).exists()
|
||||
|
||||
# Update atomic with unexpected no previous file
|
||||
content = b"content6"
|
||||
write_bytes_atomic(doc_path / file_name, content)
|
||||
assert (doc_path / file_name).read_bytes() == content
|
||||
assert (doc_app1_path / file_name).read_bytes() == content
|
||||
assert not (doc_app2_path / file_name).exists()
|
||||
|
||||
# Unlink doc on host again
|
||||
(folder_path / file_name).unlink()
|
||||
assert doc_path.exists()
|
||||
assert doc_app1_path.exists()
|
||||
assert not (doc_path / file_name).exists()
|
||||
assert not (doc_app1_path / file_name).exists()
|
||||
|
||||
# Update truncating with unexpected no previous file
|
||||
content = b"content7"
|
||||
write_bytes_trunc(doc_path / file_name, content)
|
||||
assert (doc_path / file_name).read_bytes() == content
|
||||
assert (doc_app1_path / file_name).read_bytes() == content
|
||||
assert not (doc_app2_path / file_name).exists()
|
||||
|
||||
def test_get_host_paths(self, xdg_document_portal, dbus_con):
|
||||
documents_intf = xdp.get_document_portal_iface(dbus_con)
|
||||
|
||||
content = b"content"
|
||||
file_name = "host-path"
|
||||
|
||||
file_path = Path(os.environ["TMPDIR"]) / file_name
|
||||
write_bytes_atomic(file_path, content)
|
||||
doc_id = export_file(documents_intf, file_path)
|
||||
|
||||
host_paths = documents_intf.GetHostPaths([doc_id], byte_arrays=True)
|
||||
assert doc_id in host_paths
|
||||
doc_host_path = path_from_null_term_bytes(host_paths[doc_id])
|
||||
assert doc_host_path == file_path
|
||||
|
||||
def test_host_paths_xattr(self, xdg_document_portal, dbus_con):
|
||||
documents_intf = xdp.get_document_portal_iface(dbus_con)
|
||||
mountpoint = get_mountpoint(documents_intf)
|
||||
|
||||
base_path = Path(os.environ["TMPDIR"]) / "a"
|
||||
file_path = base_path / "b" / "c"
|
||||
file_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
file_path.write_bytes(b"test")
|
||||
|
||||
doc_ids, extra = export_files(
|
||||
documents_intf, [base_path], ["read"], flags=EXPORT_FILES_FLAG_EXPORT_DIR
|
||||
)
|
||||
doc_id = doc_ids[0]
|
||||
|
||||
host_path = get_host_path_attr(mountpoint / doc_id)
|
||||
assert not host_path
|
||||
|
||||
host_path = get_host_path_attr(mountpoint / doc_id / "a")
|
||||
assert host_path == base_path
|
||||
|
||||
host_path = get_host_path_attr(mountpoint / doc_id / "a" / "b")
|
||||
assert host_path == base_path / "b"
|
||||
|
||||
host_path = get_host_path_attr(mountpoint / doc_id / "a" / "b" / "c")
|
||||
assert host_path == base_path / "b" / "c"
|
||||
|
||||
|
||||
try:
|
||||
xdp.ensure_fuse_supported()
|
||||
except xdp.FuseNotSupportedException as e:
|
||||
pytest.skip(f"No fuse support: {e}", allow_module_level=True)
|
||||
84
Telegram/ThirdParty/xdg-desktop-portal/tests/test_dynamiclauncher.py
vendored
Normal file
84
Telegram/ThirdParty/xdg-desktop-portal/tests/test_dynamiclauncher.py
vendored
Normal file
@@ -0,0 +1,84 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
#
|
||||
# This file is formatted with Python Black
|
||||
|
||||
import tests as xdp
|
||||
|
||||
import pytest
|
||||
import dbus
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
SVG_IMAGE_DATA = """<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="16px" width="16px"/>
|
||||
"""
|
||||
|
||||
DESKTOP_FILE = b"""[Desktop Entry]
|
||||
Version=1.0
|
||||
Name=Dynamic Launcher Example
|
||||
Exec=true %u
|
||||
Type=Application
|
||||
"""
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def required_templates():
|
||||
return {"dynamiclauncher": {}}
|
||||
|
||||
|
||||
class TestDynamicLauncher:
|
||||
def test_version(self, portals, dbus_con):
|
||||
"""tests the version of the interface"""
|
||||
|
||||
xdp.check_version(dbus_con, "DynamicLauncher", 1)
|
||||
|
||||
def test_basic(self, portals, dbus_con, app_id):
|
||||
"""test that the backend receives the expected data"""
|
||||
|
||||
dynlauncher_intf = xdp.get_portal_iface(dbus_con, "DynamicLauncher")
|
||||
mock_intf = xdp.get_mock_iface(dbus_con)
|
||||
|
||||
app_name = "App Name"
|
||||
bytes = SVG_IMAGE_DATA.encode("utf-8")
|
||||
|
||||
request = xdp.Request(dbus_con, dynlauncher_intf)
|
||||
options = {
|
||||
"modal": False,
|
||||
}
|
||||
response = request.call(
|
||||
"PrepareInstall",
|
||||
parent_window="",
|
||||
name=app_name,
|
||||
icon_v=dbus.Struct(
|
||||
("bytes", dbus.ByteArray(bytes, variant_level=1)),
|
||||
signature="sv",
|
||||
variant_level=1,
|
||||
),
|
||||
options=options,
|
||||
)
|
||||
|
||||
assert response
|
||||
assert response.response == 0
|
||||
assert response.results["name"] == app_name
|
||||
token = response.results["token"]
|
||||
|
||||
# Check the impl portal was called with the right args
|
||||
method_calls = mock_intf.GetMethodCalls("PrepareInstall")
|
||||
assert len(method_calls) > 0
|
||||
_, args = method_calls[-1]
|
||||
assert args[2] == "" # parent window
|
||||
assert args[3] == app_name # name
|
||||
# args[4] == icon
|
||||
assert not args[5]["modal"]
|
||||
|
||||
desktop_file_name = app_id + ".ExampleApp.desktop"
|
||||
dynlauncher_intf.Install(
|
||||
token,
|
||||
desktop_file_name,
|
||||
DESKTOP_FILE,
|
||||
{},
|
||||
)
|
||||
|
||||
file = Path(os.environ["XDG_DATA_HOME"]) / "applications" / desktop_file_name
|
||||
assert file.exists()
|
||||
293
Telegram/ThirdParty/xdg-desktop-portal/tests/test_email.py
vendored
Normal file
293
Telegram/ThirdParty/xdg-desktop-portal/tests/test_email.py
vendored
Normal file
@@ -0,0 +1,293 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
#
|
||||
# This file is formatted with Python Black
|
||||
|
||||
import tests as xdp
|
||||
|
||||
import dbus
|
||||
import pytest
|
||||
import time
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def required_templates():
|
||||
return {"email": {}}
|
||||
|
||||
|
||||
class TestEmail:
|
||||
def test_version(self, portals, dbus_con):
|
||||
"""tests the version of the interface"""
|
||||
|
||||
xdp.check_version(dbus_con, "Email", 4)
|
||||
|
||||
def test_basic(self, portals, dbus_con):
|
||||
"""test that the backend receives the expected data"""
|
||||
|
||||
email_intf = xdp.get_portal_iface(dbus_con, "Email")
|
||||
mock_intf = xdp.get_mock_iface(dbus_con)
|
||||
|
||||
addresses = ["mclasen@redhat.com"]
|
||||
subject = "Re: portal tests"
|
||||
body = "You have to see this"
|
||||
|
||||
request = xdp.Request(dbus_con, email_intf)
|
||||
options = {
|
||||
"addresses": addresses,
|
||||
"subject": subject,
|
||||
"body": body,
|
||||
}
|
||||
response = request.call(
|
||||
"ComposeEmail",
|
||||
parent_window="",
|
||||
options=options,
|
||||
)
|
||||
|
||||
assert response
|
||||
assert response.response == 0
|
||||
|
||||
# Check the impl portal was called with the right args
|
||||
method_calls = mock_intf.GetMethodCalls("ComposeEmail")
|
||||
assert len(method_calls) > 0
|
||||
_, args = method_calls[-1]
|
||||
assert args[2] == "" # parent window
|
||||
assert args[3]["addresses"] == addresses
|
||||
assert args[3]["subject"] == subject
|
||||
assert args[3]["body"] == body
|
||||
|
||||
def test_address(self, portals, dbus_con):
|
||||
"""test that an invalid address triggers an error"""
|
||||
|
||||
email_intf = xdp.get_portal_iface(dbus_con, "Email")
|
||||
mock_intf = xdp.get_mock_iface(dbus_con)
|
||||
|
||||
addresses = ["gibberish! not an email address\n%Q"]
|
||||
|
||||
request = xdp.Request(dbus_con, email_intf)
|
||||
options = {
|
||||
"addresses": addresses,
|
||||
}
|
||||
with pytest.raises(dbus.exceptions.DBusException) as excinfo:
|
||||
request.call(
|
||||
"ComposeEmail",
|
||||
parent_window="",
|
||||
options=options,
|
||||
)
|
||||
assert (
|
||||
excinfo.value.get_dbus_name()
|
||||
== "org.freedesktop.portal.Error.InvalidArgument"
|
||||
)
|
||||
|
||||
# Check the impl portal was never called
|
||||
method_calls = mock_intf.GetMethodCalls("ComposeEmail")
|
||||
assert len(method_calls) == 0
|
||||
|
||||
def test_punycode_address(self, portals, dbus_con):
|
||||
"""test email address containing punycode"""
|
||||
|
||||
email_intf = xdp.get_portal_iface(dbus_con, "Email")
|
||||
mock_intf = xdp.get_mock_iface(dbus_con)
|
||||
|
||||
addresses = ["xn--franais-xxa@exemple.fr"]
|
||||
subject = "Re: portal tests"
|
||||
body = "To ASCII and beyond"
|
||||
|
||||
request = xdp.Request(dbus_con, email_intf)
|
||||
options = {
|
||||
"addresses": addresses,
|
||||
"subject": subject,
|
||||
"body": body,
|
||||
}
|
||||
response = request.call(
|
||||
"ComposeEmail",
|
||||
parent_window="",
|
||||
options=options,
|
||||
)
|
||||
|
||||
assert response
|
||||
assert response.response == 0
|
||||
|
||||
# Check the impl portal was called with the right args
|
||||
method_calls = mock_intf.GetMethodCalls("ComposeEmail")
|
||||
assert len(method_calls) > 0
|
||||
_, args = method_calls[-1]
|
||||
assert args[2] == "" # parent window
|
||||
assert args[3]["addresses"] == addresses
|
||||
assert args[3]["subject"] == subject
|
||||
assert args[3]["body"] == body
|
||||
|
||||
def test_subject_multiline(self, portals, dbus_con):
|
||||
"""test that an multiline subject triggers an error"""
|
||||
|
||||
email_intf = xdp.get_portal_iface(dbus_con, "Email")
|
||||
mock_intf = xdp.get_mock_iface(dbus_con)
|
||||
|
||||
subject = "not\na\nvalid\nsubject line"
|
||||
|
||||
request = xdp.Request(dbus_con, email_intf)
|
||||
options = {
|
||||
"subject": subject,
|
||||
}
|
||||
with pytest.raises(dbus.exceptions.DBusException) as excinfo:
|
||||
request.call(
|
||||
"ComposeEmail",
|
||||
parent_window="",
|
||||
options=options,
|
||||
)
|
||||
assert (
|
||||
excinfo.value.get_dbus_name()
|
||||
== "org.freedesktop.portal.Error.InvalidArgument"
|
||||
)
|
||||
|
||||
# Check the impl portal was never called
|
||||
method_calls = mock_intf.GetMethodCalls("ComposeEmail")
|
||||
assert len(method_calls) == 0
|
||||
|
||||
def test_subject_too_long(self, portals, dbus_con):
|
||||
"""test that a subject line over 200 chars triggers an error"""
|
||||
|
||||
email_intf = xdp.get_portal_iface(dbus_con, "Email")
|
||||
mock_intf = xdp.get_mock_iface(dbus_con)
|
||||
|
||||
subject = "This subject line is too long" + "abc" * 60
|
||||
|
||||
assert len(subject) > 200
|
||||
|
||||
request = xdp.Request(dbus_con, email_intf)
|
||||
options = {
|
||||
"subject": subject,
|
||||
}
|
||||
with pytest.raises(dbus.exceptions.DBusException) as excinfo:
|
||||
request.call(
|
||||
"ComposeEmail",
|
||||
parent_window="",
|
||||
options=options,
|
||||
)
|
||||
assert (
|
||||
excinfo.value.get_dbus_name()
|
||||
== "org.freedesktop.portal.Error.InvalidArgument"
|
||||
)
|
||||
|
||||
# Check the impl portal was never called
|
||||
method_calls = mock_intf.GetMethodCalls("ComposeEmail")
|
||||
assert len(method_calls) == 0
|
||||
|
||||
@pytest.mark.parametrize("template_params", ({"email": {"delay": 2000}},))
|
||||
def test_delay(self, portals, dbus_con):
|
||||
"""
|
||||
Test that everything works as expected when the backend takes some
|
||||
time to send its response, as * is to be expected from a real backend
|
||||
that presents dialogs to the user.
|
||||
"""
|
||||
|
||||
email_intf = xdp.get_portal_iface(dbus_con, "Email")
|
||||
mock_intf = xdp.get_mock_iface(dbus_con)
|
||||
|
||||
subject = "delay test"
|
||||
addresses = ["mclasen@redhat.com"]
|
||||
|
||||
request = xdp.Request(dbus_con, email_intf)
|
||||
options = {
|
||||
"addresses": addresses,
|
||||
"subject": subject,
|
||||
}
|
||||
|
||||
start_time = time.perf_counter()
|
||||
|
||||
response = request.call(
|
||||
"ComposeEmail",
|
||||
parent_window="",
|
||||
options=options,
|
||||
)
|
||||
|
||||
assert response
|
||||
assert response.response == 0
|
||||
|
||||
end_time = time.perf_counter()
|
||||
|
||||
assert end_time - start_time > 2
|
||||
|
||||
# Check the impl portal was called with the right args
|
||||
method_calls = mock_intf.GetMethodCalls("ComposeEmail")
|
||||
assert len(method_calls) > 0
|
||||
_, args = method_calls[-1]
|
||||
assert args[2] == "" # parent window
|
||||
assert args[3]["addresses"] == addresses
|
||||
assert args[3]["subject"] == subject
|
||||
|
||||
@pytest.mark.parametrize("template_params", ({"email": {"response": 1}},))
|
||||
def test_cancel(self, portals, dbus_con):
|
||||
"""
|
||||
Test that user cancellation works as expected.
|
||||
We simulate that the user cancels a hypothetical dialog,
|
||||
by telling the backend to return 1 as response code.
|
||||
And we check that we get the expected G_IO_ERROR_CANCELLED.
|
||||
"""
|
||||
|
||||
email_intf = xdp.get_portal_iface(dbus_con, "Email")
|
||||
mock_intf = xdp.get_mock_iface(dbus_con)
|
||||
|
||||
subject = "cancel test"
|
||||
addresses = ["mclasen@redhat.com"]
|
||||
|
||||
request = xdp.Request(dbus_con, email_intf)
|
||||
options = {
|
||||
"addresses": addresses,
|
||||
"subject": subject,
|
||||
}
|
||||
|
||||
response = request.call(
|
||||
"ComposeEmail",
|
||||
parent_window="",
|
||||
options=options,
|
||||
)
|
||||
|
||||
assert response
|
||||
assert response.response == 1
|
||||
|
||||
# Check the impl portal was called with the right args
|
||||
method_calls = mock_intf.GetMethodCalls("ComposeEmail")
|
||||
assert len(method_calls) > 0
|
||||
_, args = method_calls[-1]
|
||||
assert args[2] == "" # parent window
|
||||
assert args[3]["addresses"] == addresses
|
||||
assert args[3]["subject"] == subject
|
||||
|
||||
@pytest.mark.parametrize("template_params", ({"email": {"expect-close": True}},))
|
||||
def test_close(self, portals, dbus_con):
|
||||
"""
|
||||
Test that app-side cancellation works as expected.
|
||||
We cancel the cancellable while while the hypothetical
|
||||
dialog is up, and tell the backend that it should
|
||||
expect a Close call. We rely on the backend to
|
||||
verify that that call actually happened.
|
||||
"""
|
||||
|
||||
email_intf = xdp.get_portal_iface(dbus_con, "Email")
|
||||
mock_intf = xdp.get_mock_iface(dbus_con)
|
||||
|
||||
subject = "close test"
|
||||
addresses = ["mclasen@redhat.com"]
|
||||
|
||||
request = xdp.Request(dbus_con, email_intf)
|
||||
request.schedule_close(1000)
|
||||
options = {
|
||||
"addresses": addresses,
|
||||
"subject": subject,
|
||||
}
|
||||
|
||||
request.call(
|
||||
"ComposeEmail",
|
||||
parent_window="",
|
||||
options=options,
|
||||
)
|
||||
|
||||
# Only true if the impl.Request was closed too
|
||||
assert request.closed
|
||||
|
||||
# Check the impl portal was called with the right args
|
||||
method_calls = mock_intf.GetMethodCalls("ComposeEmail")
|
||||
assert len(method_calls) > 0
|
||||
_, args = method_calls[-1]
|
||||
assert args[2] == "" # parent window
|
||||
assert args[3]["addresses"] == addresses
|
||||
assert args[3]["subject"] == subject
|
||||
591
Telegram/ThirdParty/xdg-desktop-portal/tests/test_filechooser.py
vendored
Normal file
591
Telegram/ThirdParty/xdg-desktop-portal/tests/test_filechooser.py
vendored
Normal file
@@ -0,0 +1,591 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
#
|
||||
# This file is formatted with Python Black
|
||||
|
||||
import tests as xdp
|
||||
|
||||
import dbus
|
||||
import pytest
|
||||
|
||||
|
||||
FILECHOOSER_RESULTS = {
|
||||
"uris": ["file:///test.txt", "file:///example/test2.txt"],
|
||||
"choices": [("encoding", "utf8"), ("reencode", "true"), ("third", "a")],
|
||||
}
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def required_templates():
|
||||
return {
|
||||
"filechooser": {
|
||||
"results": dbus.Dictionary(FILECHOOSER_RESULTS, signature="sv"),
|
||||
},
|
||||
"lockdown": {},
|
||||
}
|
||||
|
||||
|
||||
class TestFilechooser:
|
||||
def test_version(self, portals, dbus_con):
|
||||
xdp.check_version(dbus_con, "FileChooser", 4)
|
||||
|
||||
def test_open_file_basic(self, portals, dbus_con, app_id):
|
||||
filechooser_intf = xdp.get_portal_iface(dbus_con, "FileChooser")
|
||||
mock_intf = xdp.get_mock_iface(dbus_con)
|
||||
|
||||
title = "Test"
|
||||
accept_label = "Accept"
|
||||
multiple = True
|
||||
options = {
|
||||
"accept_label": accept_label,
|
||||
"multiple": multiple,
|
||||
}
|
||||
request = xdp.Request(dbus_con, filechooser_intf)
|
||||
response = request.call(
|
||||
"OpenFile",
|
||||
parent_window="",
|
||||
title=title,
|
||||
options=options,
|
||||
)
|
||||
|
||||
assert response
|
||||
assert response.response == 0
|
||||
assert response.results["uris"] == FILECHOOSER_RESULTS["uris"]
|
||||
|
||||
# Check the impl portal was called with the right args
|
||||
method_calls = mock_intf.GetMethodCalls("OpenFile")
|
||||
assert len(method_calls) == 1
|
||||
_, args = method_calls.pop()
|
||||
assert args[1] == app_id
|
||||
assert args[2] == "" # parent window
|
||||
assert args[3] == title
|
||||
assert args[4]["accept_label"] == accept_label
|
||||
assert args[4]["multiple"] == multiple
|
||||
|
||||
@pytest.mark.parametrize("template_params", ({"filechooser": {"response": 1}},))
|
||||
def test_open_file_cancel(self, portals, dbus_con, app_id):
|
||||
filechooser_intf = xdp.get_portal_iface(dbus_con, "FileChooser")
|
||||
mock_intf = xdp.get_mock_iface(dbus_con)
|
||||
|
||||
title = "Test"
|
||||
accept_label = "Accept"
|
||||
multiple = True
|
||||
options = {
|
||||
"accept_label": accept_label,
|
||||
"multiple": multiple,
|
||||
}
|
||||
request = xdp.Request(dbus_con, filechooser_intf)
|
||||
response = request.call(
|
||||
"OpenFile",
|
||||
parent_window="",
|
||||
title=title,
|
||||
options=options,
|
||||
)
|
||||
|
||||
assert response
|
||||
assert response.response == 1
|
||||
|
||||
# Check the impl portal was called with the right args
|
||||
method_calls = mock_intf.GetMethodCalls("OpenFile")
|
||||
assert len(method_calls) == 1
|
||||
_, args = method_calls.pop()
|
||||
assert args[1] == app_id
|
||||
assert args[2] == "" # parent window
|
||||
assert args[3] == title
|
||||
assert args[4]["accept_label"] == accept_label
|
||||
assert args[4]["multiple"] == multiple
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"template_params", ({"filechooser": {"expect-close": True}},)
|
||||
)
|
||||
def test_open_file_close(self, portals, dbus_con, app_id):
|
||||
filechooser_intf = xdp.get_portal_iface(dbus_con, "FileChooser")
|
||||
mock_intf = xdp.get_mock_iface(dbus_con)
|
||||
|
||||
title = "Test"
|
||||
accept_label = "Accept"
|
||||
multiple = True
|
||||
options = {
|
||||
"accept_label": accept_label,
|
||||
"multiple": multiple,
|
||||
}
|
||||
request = xdp.Request(dbus_con, filechooser_intf)
|
||||
request.schedule_close(1000)
|
||||
request.call(
|
||||
"OpenFile",
|
||||
parent_window="",
|
||||
title=title,
|
||||
options=options,
|
||||
)
|
||||
|
||||
# Only true if the impl.Request was closed too
|
||||
assert request.closed
|
||||
|
||||
# Check the impl portal was called with the right args
|
||||
method_calls = mock_intf.GetMethodCalls("OpenFile")
|
||||
assert len(method_calls) == 1
|
||||
_, args = method_calls.pop()
|
||||
assert args[1] == app_id
|
||||
assert args[2] == "" # parent window
|
||||
assert args[3] == title
|
||||
assert args[4]["accept_label"] == accept_label
|
||||
assert args[4]["multiple"] == multiple
|
||||
|
||||
def test_open_file_filter1(self, portals, dbus_con):
|
||||
filechooser_intf = xdp.get_portal_iface(dbus_con, "FileChooser")
|
||||
mock_intf = xdp.get_mock_iface(dbus_con)
|
||||
|
||||
options = {
|
||||
"filters": [
|
||||
(
|
||||
"Images",
|
||||
[
|
||||
(dbus.UInt32(0), "*ico"),
|
||||
(dbus.UInt32(1), "image/png"),
|
||||
],
|
||||
),
|
||||
(
|
||||
"Text",
|
||||
[
|
||||
(dbus.UInt32(0), "*.txt"),
|
||||
],
|
||||
),
|
||||
],
|
||||
}
|
||||
request = xdp.Request(dbus_con, filechooser_intf)
|
||||
response = request.call(
|
||||
"OpenFile",
|
||||
parent_window="",
|
||||
title="Test",
|
||||
options=options,
|
||||
)
|
||||
|
||||
assert response
|
||||
assert response.response == 0
|
||||
assert response.results["uris"] == FILECHOOSER_RESULTS["uris"]
|
||||
|
||||
method_calls = mock_intf.GetMethodCalls("OpenFile")
|
||||
assert len(method_calls) == 1
|
||||
|
||||
def test_open_file_filter2(self, portals, dbus_con):
|
||||
filechooser_intf = xdp.get_portal_iface(dbus_con, "FileChooser")
|
||||
|
||||
options = {
|
||||
"filters": [
|
||||
(
|
||||
"Text",
|
||||
[
|
||||
# Invalid filter type
|
||||
(dbus.UInt32(4), "*.txt"),
|
||||
],
|
||||
),
|
||||
],
|
||||
}
|
||||
request = xdp.Request(dbus_con, filechooser_intf)
|
||||
with pytest.raises(dbus.exceptions.DBusException) as excinfo:
|
||||
request.call(
|
||||
"OpenFile",
|
||||
parent_window="",
|
||||
title="Test",
|
||||
options=options,
|
||||
)
|
||||
assert (
|
||||
excinfo.value.get_dbus_name()
|
||||
== "org.freedesktop.portal.Error.InvalidArgument"
|
||||
)
|
||||
|
||||
def test_open_file_current_filter1(self, portals, dbus_con):
|
||||
filechooser_intf = xdp.get_portal_iface(dbus_con, "FileChooser")
|
||||
mock_intf = xdp.get_mock_iface(dbus_con)
|
||||
|
||||
options = {
|
||||
"filters": [
|
||||
(
|
||||
"Images",
|
||||
[
|
||||
(dbus.UInt32(0), "*ico"),
|
||||
(dbus.UInt32(1), "image/png"),
|
||||
],
|
||||
),
|
||||
(
|
||||
"Text",
|
||||
[
|
||||
(dbus.UInt32(0), "*.txt"),
|
||||
],
|
||||
),
|
||||
],
|
||||
"current_filter": (
|
||||
"Text",
|
||||
[
|
||||
(dbus.UInt32(0), "*.txt"),
|
||||
],
|
||||
),
|
||||
}
|
||||
request = xdp.Request(dbus_con, filechooser_intf)
|
||||
response = request.call(
|
||||
"OpenFile",
|
||||
parent_window="",
|
||||
title="Test",
|
||||
options=options,
|
||||
)
|
||||
|
||||
assert response
|
||||
assert response.response == 0
|
||||
assert response.results["uris"] == FILECHOOSER_RESULTS["uris"]
|
||||
|
||||
method_calls = mock_intf.GetMethodCalls("OpenFile")
|
||||
assert len(method_calls) == 1
|
||||
_, args = method_calls.pop()
|
||||
assert args[4]["current_filter"] == options["current_filter"]
|
||||
|
||||
def test_open_file_current_filter2(self, portals, dbus_con):
|
||||
filechooser_intf = xdp.get_portal_iface(dbus_con, "FileChooser")
|
||||
mock_intf = xdp.get_mock_iface(dbus_con)
|
||||
|
||||
options = {
|
||||
"current_filter": (
|
||||
"Text",
|
||||
[
|
||||
(dbus.UInt32(0), "*.txt"),
|
||||
],
|
||||
),
|
||||
}
|
||||
request = xdp.Request(dbus_con, filechooser_intf)
|
||||
response = request.call(
|
||||
"OpenFile",
|
||||
parent_window="",
|
||||
title="Test",
|
||||
options=options,
|
||||
)
|
||||
|
||||
assert response
|
||||
assert response.response == 0
|
||||
assert response.results["uris"] == FILECHOOSER_RESULTS["uris"]
|
||||
|
||||
method_calls = mock_intf.GetMethodCalls("OpenFile")
|
||||
assert len(method_calls) == 1
|
||||
_, args = method_calls.pop()
|
||||
assert args[4]["current_filter"] == options["current_filter"]
|
||||
|
||||
def test_open_file_current_filter3(self, portals, dbus_con):
|
||||
filechooser_intf = xdp.get_portal_iface(dbus_con, "FileChooser")
|
||||
|
||||
options = {
|
||||
"current_filter": (
|
||||
"Text",
|
||||
[
|
||||
# Invalid filter type
|
||||
(dbus.UInt32(6), "*.txt"),
|
||||
],
|
||||
),
|
||||
}
|
||||
request = xdp.Request(dbus_con, filechooser_intf)
|
||||
with pytest.raises(dbus.exceptions.DBusException) as excinfo:
|
||||
request.call(
|
||||
"OpenFile",
|
||||
parent_window="",
|
||||
title="Test",
|
||||
options=options,
|
||||
)
|
||||
assert (
|
||||
excinfo.value.get_dbus_name()
|
||||
== "org.freedesktop.portal.Error.InvalidArgument"
|
||||
)
|
||||
|
||||
def test_open_file_current_filter4(self, portals, dbus_con):
|
||||
filechooser_intf = xdp.get_portal_iface(dbus_con, "FileChooser")
|
||||
|
||||
options = {
|
||||
"filters": [
|
||||
(
|
||||
"Images",
|
||||
[
|
||||
(dbus.UInt32(0), "*ico"),
|
||||
(dbus.UInt32(1), "image/png"),
|
||||
],
|
||||
),
|
||||
(
|
||||
"Text",
|
||||
[
|
||||
(dbus.UInt32(0), "*.txt"),
|
||||
],
|
||||
),
|
||||
],
|
||||
"current_filter": (
|
||||
"Something else",
|
||||
[
|
||||
(dbus.UInt32(0), "*.sth.else"),
|
||||
],
|
||||
),
|
||||
}
|
||||
request = xdp.Request(dbus_con, filechooser_intf)
|
||||
with pytest.raises(dbus.exceptions.DBusException) as excinfo:
|
||||
request.call(
|
||||
"OpenFile",
|
||||
parent_window="",
|
||||
title="Test",
|
||||
options=options,
|
||||
)
|
||||
assert (
|
||||
excinfo.value.get_dbus_name()
|
||||
== "org.freedesktop.portal.Error.InvalidArgument"
|
||||
)
|
||||
|
||||
def test_open_file_choices1(self, portals, dbus_con):
|
||||
filechooser_intf = xdp.get_portal_iface(dbus_con, "FileChooser")
|
||||
mock_intf = xdp.get_mock_iface(dbus_con)
|
||||
|
||||
options = {
|
||||
"choices": [
|
||||
(
|
||||
"encoding",
|
||||
"Encoding",
|
||||
[
|
||||
("utf8", "Unicode"),
|
||||
("latin15", "Western"),
|
||||
],
|
||||
"latin15",
|
||||
),
|
||||
(
|
||||
"reencode",
|
||||
"Reencode",
|
||||
[],
|
||||
"false",
|
||||
),
|
||||
(
|
||||
"third",
|
||||
"Third",
|
||||
[("a", "A"), ("b", "B")],
|
||||
"",
|
||||
),
|
||||
],
|
||||
}
|
||||
request = xdp.Request(dbus_con, filechooser_intf)
|
||||
response = request.call(
|
||||
"OpenFile",
|
||||
parent_window="",
|
||||
title="Test",
|
||||
options=options,
|
||||
)
|
||||
|
||||
assert response
|
||||
assert response.response == 0
|
||||
assert response.results["uris"] == FILECHOOSER_RESULTS["uris"]
|
||||
|
||||
method_calls = mock_intf.GetMethodCalls("OpenFile")
|
||||
assert len(method_calls) == 1
|
||||
_, args = method_calls.pop()
|
||||
assert args[4]["choices"] == options["choices"]
|
||||
|
||||
def test_open_file_choices_invalid(self, portals, dbus_con):
|
||||
filechooser_intf = xdp.get_portal_iface(dbus_con, "FileChooser")
|
||||
|
||||
invalid_choices = [
|
||||
(
|
||||
"encoding",
|
||||
"Encoding",
|
||||
[
|
||||
("utf8", ""),
|
||||
("latin15", "Western"),
|
||||
],
|
||||
"latin15",
|
||||
),
|
||||
(
|
||||
"encoding",
|
||||
"Encoding",
|
||||
[
|
||||
("", "Unicode"),
|
||||
("latin15", "Western"),
|
||||
],
|
||||
"latin15",
|
||||
),
|
||||
(
|
||||
"",
|
||||
"Encoding",
|
||||
[
|
||||
("utf8", "Unicode"),
|
||||
("latin15", "Western"),
|
||||
],
|
||||
"latin15",
|
||||
),
|
||||
]
|
||||
|
||||
for choice in invalid_choices:
|
||||
request = xdp.Request(dbus_con, filechooser_intf)
|
||||
with pytest.raises(dbus.exceptions.DBusException) as excinfo:
|
||||
options = {
|
||||
"choices": [choice],
|
||||
}
|
||||
request.call(
|
||||
"OpenFile",
|
||||
parent_window="",
|
||||
title="Test",
|
||||
options=options,
|
||||
)
|
||||
assert (
|
||||
excinfo.value.get_dbus_name()
|
||||
== "org.freedesktop.portal.Error.InvalidArgument"
|
||||
)
|
||||
|
||||
def test_save_file_basic(self, portals, dbus_con, app_id):
|
||||
filechooser_intf = xdp.get_portal_iface(dbus_con, "FileChooser")
|
||||
mock_intf = xdp.get_mock_iface(dbus_con)
|
||||
|
||||
title = "Test"
|
||||
accept_label = "Accept"
|
||||
current_name = "File Name"
|
||||
options = {
|
||||
"accept_label": accept_label,
|
||||
"current_name": current_name,
|
||||
}
|
||||
request = xdp.Request(dbus_con, filechooser_intf)
|
||||
response = request.call(
|
||||
"SaveFile",
|
||||
parent_window="",
|
||||
title=title,
|
||||
options=options,
|
||||
)
|
||||
|
||||
assert response
|
||||
assert response.response == 0
|
||||
assert response.results["uris"] == FILECHOOSER_RESULTS["uris"]
|
||||
|
||||
# Check the impl portal was called with the right args
|
||||
method_calls = mock_intf.GetMethodCalls("SaveFile")
|
||||
assert len(method_calls) == 1
|
||||
_, args = method_calls.pop()
|
||||
assert args[1] == app_id
|
||||
assert args[2] == "" # parent window
|
||||
assert args[3] == title
|
||||
assert args[4]["accept_label"] == accept_label
|
||||
assert args[4]["current_name"] == current_name
|
||||
|
||||
@pytest.mark.parametrize("template_params", ({"filechooser": {"response": 1}},))
|
||||
def test_save_file_cancel(self, portals, dbus_con, app_id):
|
||||
filechooser_intf = xdp.get_portal_iface(dbus_con, "FileChooser")
|
||||
mock_intf = xdp.get_mock_iface(dbus_con)
|
||||
|
||||
title = "Test"
|
||||
accept_label = "Accept"
|
||||
current_name = "File Name"
|
||||
options = {
|
||||
"accept_label": accept_label,
|
||||
"current_name": current_name,
|
||||
}
|
||||
request = xdp.Request(dbus_con, filechooser_intf)
|
||||
response = request.call(
|
||||
"SaveFile",
|
||||
parent_window="",
|
||||
title=title,
|
||||
options=options,
|
||||
)
|
||||
|
||||
assert response
|
||||
assert response.response == 1
|
||||
|
||||
# Check the impl portal was called with the right args
|
||||
method_calls = mock_intf.GetMethodCalls("SaveFile")
|
||||
assert len(method_calls) == 1
|
||||
_, args = method_calls.pop()
|
||||
assert args[1] == app_id
|
||||
assert args[2] == "" # parent window
|
||||
assert args[3] == title
|
||||
assert args[4]["accept_label"] == accept_label
|
||||
assert args[4]["current_name"] == current_name
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"template_params", ({"filechooser": {"expect-close": True}},)
|
||||
)
|
||||
def test_save_file_close(self, portals, dbus_con, app_id):
|
||||
filechooser_intf = xdp.get_portal_iface(dbus_con, "FileChooser")
|
||||
mock_intf = xdp.get_mock_iface(dbus_con)
|
||||
|
||||
title = "Test"
|
||||
accept_label = "Accept"
|
||||
current_name = "File Name"
|
||||
options = {
|
||||
"accept_label": accept_label,
|
||||
"current_name": current_name,
|
||||
}
|
||||
request = xdp.Request(dbus_con, filechooser_intf)
|
||||
request.schedule_close(1000)
|
||||
request.call(
|
||||
"SaveFile",
|
||||
parent_window="",
|
||||
title=title,
|
||||
options=options,
|
||||
)
|
||||
|
||||
# Only true if the impl.Request was closed too
|
||||
assert request.closed
|
||||
|
||||
# Check the impl portal was called with the right args
|
||||
method_calls = mock_intf.GetMethodCalls("SaveFile")
|
||||
assert len(method_calls) == 1
|
||||
_, args = method_calls.pop()
|
||||
assert args[1] == app_id
|
||||
assert args[2] == "" # parent window
|
||||
assert args[3] == title
|
||||
assert args[4]["accept_label"] == accept_label
|
||||
assert args[4]["current_name"] == current_name
|
||||
|
||||
def test_save_file_filters(self, portals, dbus_con, app_id):
|
||||
filechooser_intf = xdp.get_portal_iface(dbus_con, "FileChooser")
|
||||
mock_intf = xdp.get_mock_iface(dbus_con)
|
||||
|
||||
options = {
|
||||
"filters": [
|
||||
(
|
||||
"Images",
|
||||
[
|
||||
(dbus.UInt32(0), "*ico"),
|
||||
(dbus.UInt32(1), "image/png"),
|
||||
],
|
||||
),
|
||||
(
|
||||
"Text",
|
||||
[
|
||||
(dbus.UInt32(0), "*.txt"),
|
||||
],
|
||||
),
|
||||
],
|
||||
}
|
||||
request = xdp.Request(dbus_con, filechooser_intf)
|
||||
response = request.call(
|
||||
"SaveFile",
|
||||
parent_window="",
|
||||
title="Title",
|
||||
options=options,
|
||||
)
|
||||
|
||||
assert response
|
||||
assert response.response == 0
|
||||
assert response.results["uris"] == FILECHOOSER_RESULTS["uris"]
|
||||
|
||||
# Check the impl portal was called with the right args
|
||||
method_calls = mock_intf.GetMethodCalls("SaveFile")
|
||||
assert len(method_calls) == 1
|
||||
_, args = method_calls.pop()
|
||||
assert args[4]["filters"] == options["filters"]
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"template_params", ({"lockdown": {"disable-save-to-disk": True}},)
|
||||
)
|
||||
def test_save_file_lockdown(self, portals, dbus_con, app_id):
|
||||
filechooser_intf = xdp.get_portal_iface(dbus_con, "FileChooser")
|
||||
mock_intf = xdp.get_mock_iface(dbus_con)
|
||||
|
||||
request = xdp.Request(dbus_con, filechooser_intf)
|
||||
with pytest.raises(dbus.exceptions.DBusException) as excinfo:
|
||||
request.call(
|
||||
"SaveFile",
|
||||
parent_window="",
|
||||
title="Title",
|
||||
options={},
|
||||
)
|
||||
assert (
|
||||
excinfo.value.get_dbus_name() == "org.freedesktop.portal.Error.NotAllowed"
|
||||
)
|
||||
|
||||
# Check the impl portal was not called
|
||||
method_calls = mock_intf.GetMethodCalls("FileChooser")
|
||||
assert len(method_calls) == 0
|
||||
216
Telegram/ThirdParty/xdg-desktop-portal/tests/test_globalshortcuts.py
vendored
Normal file
216
Telegram/ThirdParty/xdg-desktop-portal/tests/test_globalshortcuts.py
vendored
Normal file
@@ -0,0 +1,216 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
#
|
||||
# This file is formatted with Python Black
|
||||
|
||||
import tests as xdp
|
||||
|
||||
import dbus
|
||||
import pytest
|
||||
import time
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def required_templates():
|
||||
return {"globalshortcuts": {}}
|
||||
|
||||
|
||||
class TestGlobalShortcuts:
|
||||
def test_version(self, portals, dbus_con):
|
||||
xdp.check_version(dbus_con, "GlobalShortcuts", 1)
|
||||
|
||||
def test_create_close_session(self, portals, dbus_con, app_id):
|
||||
globalshortcuts_intf = xdp.get_portal_iface(dbus_con, "GlobalShortcuts")
|
||||
mock_intf = xdp.get_mock_iface(dbus_con)
|
||||
|
||||
request = xdp.Request(dbus_con, globalshortcuts_intf)
|
||||
options = {
|
||||
"session_handle_token": "session_token0",
|
||||
}
|
||||
response = request.call(
|
||||
"CreateSession",
|
||||
options=options,
|
||||
)
|
||||
|
||||
assert response
|
||||
assert response.response == 0
|
||||
|
||||
session = xdp.Session.from_response(dbus_con, response)
|
||||
# Check the impl portal was called with the right args
|
||||
method_calls = mock_intf.GetMethodCalls("CreateSession")
|
||||
assert len(method_calls) > 0
|
||||
_, args = method_calls[-1]
|
||||
assert args[1] == session.handle
|
||||
assert args[2] == app_id
|
||||
|
||||
session.close()
|
||||
xdp.wait_for(lambda: session.closed)
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"template_params", ({"globalshortcuts": {"force-close": 500}},)
|
||||
)
|
||||
def test_create_session_signal_closed(self, portals, dbus_con, app_id):
|
||||
globalshortcuts_intf = xdp.get_portal_iface(dbus_con, "GlobalShortcuts")
|
||||
mock_intf = xdp.get_mock_iface(dbus_con)
|
||||
|
||||
request = xdp.Request(dbus_con, globalshortcuts_intf)
|
||||
options = {
|
||||
"session_handle_token": "session_token0",
|
||||
}
|
||||
response = request.call(
|
||||
"CreateSession",
|
||||
options=options,
|
||||
)
|
||||
|
||||
assert response
|
||||
assert response.response == 0
|
||||
|
||||
session = xdp.Session.from_response(dbus_con, response)
|
||||
# Check the impl portal was called with the right args
|
||||
method_calls = mock_intf.GetMethodCalls("CreateSession")
|
||||
assert len(method_calls) > 0
|
||||
_, args = method_calls[-1]
|
||||
assert args[1] == session.handle
|
||||
assert args[2] == app_id
|
||||
|
||||
# Now expect the backend to close it
|
||||
xdp.wait_for(lambda: session.closed)
|
||||
|
||||
def test_bind_list_shortcuts(self, portals, dbus_con):
|
||||
globalshortcuts_intf = xdp.get_portal_iface(dbus_con, "GlobalShortcuts")
|
||||
|
||||
request = xdp.Request(dbus_con, globalshortcuts_intf)
|
||||
options = {
|
||||
"session_handle_token": "session_token0",
|
||||
}
|
||||
response = request.call(
|
||||
"CreateSession",
|
||||
options=options,
|
||||
)
|
||||
|
||||
assert response
|
||||
assert response.response == 0
|
||||
|
||||
session = xdp.Session.from_response(dbus_con, response)
|
||||
|
||||
shortcuts = [
|
||||
(
|
||||
"binding1",
|
||||
{
|
||||
"description": dbus.String("Binding #1", variant_level=1),
|
||||
"preferred-trigger": dbus.String("CTRL+a", variant_level=1),
|
||||
},
|
||||
),
|
||||
(
|
||||
"binding2",
|
||||
{
|
||||
"description": dbus.String("Binding #2", variant_level=1),
|
||||
"preferred-trigger": dbus.String("CTRL+b", variant_level=1),
|
||||
},
|
||||
),
|
||||
]
|
||||
|
||||
request = xdp.Request(dbus_con, globalshortcuts_intf)
|
||||
response = request.call(
|
||||
"BindShortcuts",
|
||||
session_handle=session.handle,
|
||||
shortcuts=shortcuts,
|
||||
parent_window="",
|
||||
options={},
|
||||
)
|
||||
|
||||
assert response
|
||||
assert response.response == 0
|
||||
|
||||
request = xdp.Request(dbus_con, globalshortcuts_intf)
|
||||
options = {}
|
||||
response = request.call(
|
||||
"ListShortcuts",
|
||||
session_handle=session.handle,
|
||||
options=options,
|
||||
)
|
||||
|
||||
assert response
|
||||
assert response.response == 0
|
||||
|
||||
assert len(list(response.results["shortcuts"])) == len(list(shortcuts))
|
||||
|
||||
session.close()
|
||||
xdp.wait_for(lambda: session.closed)
|
||||
|
||||
def test_trigger(self, portals, dbus_con):
|
||||
globalshortcuts_intf = xdp.get_portal_iface(dbus_con, "GlobalShortcuts")
|
||||
mock_intf = xdp.get_mock_iface(dbus_con)
|
||||
|
||||
request = xdp.Request(dbus_con, globalshortcuts_intf)
|
||||
options = {
|
||||
"session_handle_token": "session_token0",
|
||||
}
|
||||
response = request.call(
|
||||
"CreateSession",
|
||||
options=options,
|
||||
)
|
||||
|
||||
assert response
|
||||
assert response.response == 0
|
||||
|
||||
session = xdp.Session.from_response(dbus_con, response)
|
||||
|
||||
shortcuts = [
|
||||
(
|
||||
"binding1",
|
||||
{
|
||||
"description": dbus.String("Binding #1", variant_level=1),
|
||||
"preferred-trigger": dbus.String("CTRL+a", variant_level=1),
|
||||
},
|
||||
),
|
||||
]
|
||||
|
||||
request = xdp.Request(dbus_con, globalshortcuts_intf)
|
||||
response = request.call(
|
||||
"BindShortcuts",
|
||||
session_handle=session.handle,
|
||||
shortcuts=shortcuts,
|
||||
parent_window="",
|
||||
options={},
|
||||
)
|
||||
|
||||
assert response
|
||||
assert response.response == 0
|
||||
|
||||
activated_count = 0
|
||||
deactivated_count = 0
|
||||
|
||||
def cb_activated(session_handle, shortcut_id, timestamp, options):
|
||||
nonlocal activated_count
|
||||
now_since_epoch = int(time.time() * 1000000)
|
||||
# This assert will race twice a year on systems configured with
|
||||
# summer time timezone changes
|
||||
assert (
|
||||
now_since_epoch > timestamp
|
||||
and (now_since_epoch - 10 * 10001000) < timestamp
|
||||
)
|
||||
assert shortcut_id == "binding1"
|
||||
activated_count += 1
|
||||
|
||||
def cb_deactivated(session_handle, shortcut_id, timestamp, options):
|
||||
nonlocal deactivated_count
|
||||
now_since_epoch = int(time.time() * 1000000)
|
||||
# This assert will race twice a year on systems configured with
|
||||
# summer time timezone changes
|
||||
assert (
|
||||
now_since_epoch > timestamp
|
||||
and (now_since_epoch - 10 * 10001000) < timestamp
|
||||
)
|
||||
assert shortcut_id == "binding1"
|
||||
deactivated_count += 1
|
||||
|
||||
globalshortcuts_intf.connect_to_signal("Activated", cb_activated)
|
||||
globalshortcuts_intf.connect_to_signal("Deactivated", cb_deactivated)
|
||||
|
||||
mock_intf.Trigger(session.handle, "binding1")
|
||||
|
||||
xdp.wait_for(lambda: activated_count == 1 and deactivated_count == 1)
|
||||
assert not session.closed
|
||||
|
||||
session.close()
|
||||
xdp.wait_for(lambda: session.closed)
|
||||
242
Telegram/ThirdParty/xdg-desktop-portal/tests/test_inhibit.py
vendored
Normal file
242
Telegram/ThirdParty/xdg-desktop-portal/tests/test_inhibit.py
vendored
Normal file
@@ -0,0 +1,242 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
#
|
||||
# This file is formatted with Python Black
|
||||
|
||||
import tests as xdp
|
||||
|
||||
import pytest
|
||||
from enum import Enum, Flag
|
||||
|
||||
|
||||
class InhibitFlags(Flag):
|
||||
LOGOUT = 1
|
||||
USER_SWITCH = 2
|
||||
SUSPEND = 4
|
||||
IDLE = 8
|
||||
ALL = 16 - 1
|
||||
|
||||
|
||||
class SessionState(Enum):
|
||||
RUNNING = 1
|
||||
QUERY_END = 2
|
||||
ENDING = 3
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def required_templates():
|
||||
return {"inhibit": {}}
|
||||
|
||||
|
||||
class TestInhibit:
|
||||
def set_permissions(self, dbus_con, app_id, permissions):
|
||||
perm_store_intf = xdp.get_permission_store_iface(dbus_con)
|
||||
perm_store_intf.SetPermission(
|
||||
"inhibit",
|
||||
True,
|
||||
"inhibit",
|
||||
app_id,
|
||||
permissions,
|
||||
)
|
||||
|
||||
def test_version(self, portals, dbus_con):
|
||||
xdp.check_version(dbus_con, "Inhibit", 3)
|
||||
|
||||
def test_basic(self, portals, dbus_con, app_id):
|
||||
inhibit_intf = xdp.get_portal_iface(dbus_con, "Inhibit")
|
||||
mock_intf = xdp.get_mock_iface(dbus_con)
|
||||
|
||||
reason = "reason"
|
||||
flags = InhibitFlags.ALL
|
||||
|
||||
request = xdp.Request(dbus_con, inhibit_intf)
|
||||
options = {
|
||||
"reason": reason,
|
||||
}
|
||||
response = request.call(
|
||||
"Inhibit",
|
||||
window="",
|
||||
flags=flags.value,
|
||||
options=options,
|
||||
)
|
||||
|
||||
assert response
|
||||
assert response.response == 0
|
||||
|
||||
# Check the impl portal was called with the right args
|
||||
method_calls = mock_intf.GetMethodCalls("Inhibit")
|
||||
assert len(method_calls) > 0
|
||||
_, args = method_calls[-1]
|
||||
assert args[1] == app_id
|
||||
assert args[2] == "" # parent window
|
||||
assert args[3] == flags.value
|
||||
assert args[4]["reason"] == reason
|
||||
|
||||
@pytest.mark.parametrize("template_params", ({"inhibit": {"response": 1}},))
|
||||
def test_cancel(self, portals, dbus_con, app_id):
|
||||
inhibit_intf = xdp.get_portal_iface(dbus_con, "Inhibit")
|
||||
mock_intf = xdp.get_mock_iface(dbus_con)
|
||||
|
||||
reason = "reason"
|
||||
flags = InhibitFlags.ALL
|
||||
|
||||
request = xdp.Request(dbus_con, inhibit_intf)
|
||||
options = {
|
||||
"reason": reason,
|
||||
}
|
||||
response = request.call(
|
||||
"Inhibit",
|
||||
window="",
|
||||
flags=flags.value,
|
||||
options=options,
|
||||
)
|
||||
|
||||
# for some reason, the backend failing is still considered a success
|
||||
assert response
|
||||
assert response.response == 0
|
||||
|
||||
# Check the impl portal was called with the right args
|
||||
method_calls = mock_intf.GetMethodCalls("Inhibit")
|
||||
assert len(method_calls) > 0
|
||||
_, args = method_calls[-1]
|
||||
assert args[1] == app_id
|
||||
assert args[2] == "" # parent window
|
||||
assert args[3] == flags.value
|
||||
assert args[4]["reason"] == reason
|
||||
|
||||
@pytest.mark.parametrize("template_params", ({"inhibit": {"expect-close": True}},))
|
||||
def test_close(self, portals, dbus_con, app_id):
|
||||
inhibit_intf = xdp.get_portal_iface(dbus_con, "Inhibit")
|
||||
mock_intf = xdp.get_mock_iface(dbus_con)
|
||||
|
||||
reason = "reason"
|
||||
flags = InhibitFlags.ALL
|
||||
|
||||
request = xdp.Request(dbus_con, inhibit_intf)
|
||||
request.schedule_close(1000)
|
||||
options = {
|
||||
"reason": reason,
|
||||
}
|
||||
request.call(
|
||||
"Inhibit",
|
||||
window="",
|
||||
flags=flags.value,
|
||||
options=options,
|
||||
)
|
||||
|
||||
# Only true if the impl.Request was closed too
|
||||
assert request.closed
|
||||
|
||||
# Check the impl portal was called with the right args
|
||||
method_calls = mock_intf.GetMethodCalls("Inhibit")
|
||||
assert len(method_calls) > 0
|
||||
_, args = method_calls[-1]
|
||||
assert args[1] == app_id
|
||||
assert args[2] == "" # parent window
|
||||
assert args[3] == flags.value
|
||||
assert args[4]["reason"] == reason
|
||||
|
||||
def test_permission(self, portals, dbus_con, app_id):
|
||||
inhibit_intf = xdp.get_portal_iface(dbus_con, "Inhibit")
|
||||
mock_intf = xdp.get_mock_iface(dbus_con)
|
||||
|
||||
self.set_permissions(dbus_con, app_id, ["logout", "suspend"])
|
||||
|
||||
reason = "reason"
|
||||
flags = InhibitFlags.LOGOUT | InhibitFlags.SUSPEND | InhibitFlags.IDLE
|
||||
allowed_flags = InhibitFlags.LOGOUT | InhibitFlags.SUSPEND
|
||||
|
||||
request = xdp.Request(dbus_con, inhibit_intf)
|
||||
options = {
|
||||
"reason": reason,
|
||||
}
|
||||
response = request.call(
|
||||
"Inhibit",
|
||||
window="",
|
||||
flags=flags.value,
|
||||
options=options,
|
||||
)
|
||||
|
||||
assert response
|
||||
assert response.response == 0
|
||||
|
||||
method_calls = mock_intf.GetMethodCalls("Inhibit")
|
||||
_, args = method_calls[-1]
|
||||
assert args[3] == allowed_flags.value
|
||||
|
||||
self.set_permissions(dbus_con, app_id, ["suspend"])
|
||||
|
||||
flags = InhibitFlags.LOGOUT | InhibitFlags.SUSPEND | InhibitFlags.IDLE
|
||||
allowed_flags = InhibitFlags.SUSPEND
|
||||
|
||||
request = xdp.Request(dbus_con, inhibit_intf)
|
||||
options = {
|
||||
"reason": reason,
|
||||
}
|
||||
response = request.call(
|
||||
"Inhibit",
|
||||
window="",
|
||||
flags=flags.value,
|
||||
options=options,
|
||||
)
|
||||
|
||||
assert response
|
||||
assert response.response == 0
|
||||
|
||||
method_calls = mock_intf.GetMethodCalls("Inhibit")
|
||||
_, args = method_calls[-1]
|
||||
assert args[3] == allowed_flags.value
|
||||
|
||||
def test_monitor(self, portals, dbus_con, app_id):
|
||||
inhibit_intf = xdp.get_portal_iface(dbus_con, "Inhibit")
|
||||
mock_intf = xdp.get_mock_iface(dbus_con)
|
||||
|
||||
changed_count = 0
|
||||
|
||||
request = xdp.Request(dbus_con, inhibit_intf)
|
||||
options = {
|
||||
"session_handle_token": "session_token0",
|
||||
}
|
||||
response = request.call(
|
||||
"CreateMonitor",
|
||||
window="",
|
||||
options=options,
|
||||
)
|
||||
|
||||
assert response
|
||||
assert response.response == 0
|
||||
|
||||
session = xdp.Session.from_response(dbus_con, response)
|
||||
|
||||
# Check the impl portal was called with the right args
|
||||
method_calls = mock_intf.GetMethodCalls("CreateMonitor")
|
||||
assert len(method_calls) > 0
|
||||
_, args = method_calls[-1]
|
||||
assert args[1] == session.handle
|
||||
assert args[2] == app_id
|
||||
assert args[3] == "" # parent window
|
||||
|
||||
def state_changed_cb(session_handle, state):
|
||||
nonlocal changed_count
|
||||
|
||||
assert not state["screensaver-active"]
|
||||
assert state["session-state"] == SessionState.QUERY_END.value
|
||||
|
||||
changed_count += 1
|
||||
|
||||
inhibit_intf.connect_to_signal("StateChanged", state_changed_cb)
|
||||
|
||||
# wait for a Query End state change
|
||||
xdp.wait_for(lambda: changed_count == 1)
|
||||
assert not session.closed
|
||||
# and respond with QueryEndResponse
|
||||
inhibit_intf.QueryEndResponse(session.handle)
|
||||
|
||||
# wait for another Query End state change
|
||||
xdp.wait_for(lambda: changed_count == 2)
|
||||
assert not session.closed
|
||||
|
||||
# do not respond with QueryEndResponse and instead wait for >1s
|
||||
xdp.wait(1500)
|
||||
|
||||
# the session should have gotten closed by now
|
||||
assert session.closed
|
||||
674
Telegram/ThirdParty/xdg-desktop-portal/tests/test_inputcapture.py
vendored
Normal file
674
Telegram/ThirdParty/xdg-desktop-portal/tests/test_inputcapture.py
vendored
Normal file
@@ -0,0 +1,674 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
#
|
||||
# This file is formatted with Python Black
|
||||
|
||||
import tests as xdp
|
||||
|
||||
import dbus
|
||||
import pytest
|
||||
import socket
|
||||
from gi.repository import GLib
|
||||
from itertools import count
|
||||
from typing import Any
|
||||
|
||||
|
||||
counter = count()
|
||||
|
||||
|
||||
def default_zones():
|
||||
return [(1024, 768, 0, 0), (640, 480, 1024, 0)]
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def required_templates():
|
||||
return {"inputcapture": {}}
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def zones():
|
||||
return default_zones()
|
||||
|
||||
|
||||
class TestInputCapture:
|
||||
def create_session(self, dbus_con, capabilities=0xF):
|
||||
"""
|
||||
Call CreateSession for the given capabilities and return the
|
||||
(response, results) tuple.
|
||||
"""
|
||||
inputcapture_intf = xdp.get_portal_iface(dbus_con, "InputCapture")
|
||||
mock_intf = xdp.get_mock_iface(dbus_con)
|
||||
|
||||
request = xdp.Request(dbus_con, inputcapture_intf)
|
||||
|
||||
capabilities = dbus.UInt32(capabilities, variant_level=1)
|
||||
session_handle_token = dbus.String(f"session{next(counter)}", variant_level=1)
|
||||
|
||||
options = dbus.Dictionary(
|
||||
{
|
||||
"capabilities": capabilities,
|
||||
"session_handle_token": session_handle_token,
|
||||
},
|
||||
signature="sv",
|
||||
)
|
||||
|
||||
response = request.call("CreateSession", parent_window="", options=options)
|
||||
|
||||
assert response
|
||||
assert response.response == 0
|
||||
assert "session_handle" in response.results
|
||||
assert "capabilities" in response.results
|
||||
caps = response.results["capabilities"]
|
||||
# Returned capabilities must be a subset of the requested ones
|
||||
assert caps & ~capabilities == 0
|
||||
|
||||
self.current_session_handle = response.results["session_handle"]
|
||||
|
||||
# Check the impl portal was called with the right args
|
||||
method_calls = mock_intf.GetMethodCalls("CreateSession")
|
||||
assert len(method_calls) > 0
|
||||
_, args = method_calls[-1]
|
||||
assert args[3] == "" # parent window
|
||||
assert args[4]["capabilities"] == capabilities
|
||||
|
||||
return response
|
||||
|
||||
def get_zones(self, dbus_con):
|
||||
"""
|
||||
Call GetZones and return the (response, results) tuple.
|
||||
"""
|
||||
inputcapture_intf = xdp.get_portal_iface(dbus_con, "InputCapture")
|
||||
mock_intf = xdp.get_mock_iface(dbus_con)
|
||||
|
||||
request = xdp.Request(dbus_con, inputcapture_intf)
|
||||
options: Any = {}
|
||||
response = request.call(
|
||||
"GetZones", session_handle=self.current_session_handle, options=options
|
||||
)
|
||||
|
||||
assert response
|
||||
assert response.response == 0
|
||||
assert "zones" in response.results
|
||||
assert "zone_set" in response.results
|
||||
|
||||
self.current_zone_set = response.results["zone_set"]
|
||||
|
||||
# Check the impl portal was called with the right args
|
||||
method_calls = mock_intf.GetMethodCalls("GetZones")
|
||||
assert len(method_calls) > 0
|
||||
_, args = method_calls[-1]
|
||||
assert args[0] == request.handle
|
||||
assert args[1] == self.current_session_handle
|
||||
|
||||
return response
|
||||
|
||||
def set_pointer_barriers(self, dbus_con, barriers):
|
||||
inputcapture_intf = xdp.get_portal_iface(dbus_con, "InputCapture")
|
||||
mock_intf = xdp.get_mock_iface(dbus_con)
|
||||
|
||||
request = xdp.Request(dbus_con, inputcapture_intf)
|
||||
options: Any = {}
|
||||
response = request.call(
|
||||
"SetPointerBarriers",
|
||||
session_handle=self.current_session_handle,
|
||||
options=options,
|
||||
barriers=barriers,
|
||||
zone_set=self.current_zone_set,
|
||||
)
|
||||
assert response
|
||||
assert response.response == 0
|
||||
assert "failed_barriers" in response.results
|
||||
|
||||
# Check the impl portal was called with the right args
|
||||
method_calls = mock_intf.GetMethodCalls("SetPointerBarriers")
|
||||
assert len(method_calls) > 0
|
||||
_, args = method_calls[-1]
|
||||
assert args[0] == request.handle
|
||||
assert args[1] == self.current_session_handle
|
||||
assert args[4] == barriers
|
||||
assert args[5] == self.current_zone_set
|
||||
|
||||
return response
|
||||
|
||||
def connect_to_eis(self, dbus_con):
|
||||
inputcapture_intf = xdp.get_portal_iface(dbus_con, "InputCapture")
|
||||
mock_intf = xdp.get_mock_iface(dbus_con)
|
||||
|
||||
fd = inputcapture_intf.ConnectToEIS(
|
||||
self.current_session_handle, dbus.Dictionary({}, signature="sv")
|
||||
)
|
||||
|
||||
# Our dbusmock template sends HELLO
|
||||
eis_socket = socket.fromfd(fd.take(), socket.AF_UNIX, socket.SOCK_STREAM)
|
||||
hello = eis_socket.recv(10)
|
||||
assert hello == b"HELLO"
|
||||
|
||||
method_calls = mock_intf.GetMethodCalls("ConnectToEIS")
|
||||
assert len(method_calls) > 0
|
||||
_, args = method_calls[-1]
|
||||
assert args[0] == self.current_session_handle
|
||||
|
||||
return eis_socket
|
||||
|
||||
def enable(self, dbus_con):
|
||||
inputcapture_intf = xdp.get_portal_iface(dbus_con, "InputCapture")
|
||||
mock_intf = xdp.get_mock_iface(dbus_con)
|
||||
|
||||
inputcapture_intf.Enable(
|
||||
self.current_session_handle, dbus.Dictionary({}, signature="sv")
|
||||
)
|
||||
|
||||
method_calls = mock_intf.GetMethodCalls("Enable")
|
||||
assert len(method_calls) > 0
|
||||
_, args = method_calls[-1]
|
||||
assert args[0] == self.current_session_handle
|
||||
|
||||
def disable(self, dbus_con):
|
||||
inputcapture_intf = xdp.get_portal_iface(dbus_con, "InputCapture")
|
||||
mock_intf = xdp.get_mock_iface(dbus_con)
|
||||
|
||||
inputcapture_intf.Disable(
|
||||
self.current_session_handle, dbus.Dictionary({}, signature="sv")
|
||||
)
|
||||
|
||||
method_calls = mock_intf.GetMethodCalls("Disable")
|
||||
assert len(method_calls) > 0
|
||||
_, args = method_calls[-1]
|
||||
assert args[0] == self.current_session_handle
|
||||
|
||||
def release(self, dbus_con, activation_id: int, cursor_position=None):
|
||||
inputcapture_intf = xdp.get_portal_iface(dbus_con, "InputCapture")
|
||||
mock_intf = xdp.get_mock_iface(dbus_con)
|
||||
|
||||
options = {"activation_id": dbus.UInt32(activation_id)}
|
||||
if cursor_position:
|
||||
options["cursor_position"] = dbus.Struct(
|
||||
list(cursor_position), signature="dd", variant_level=1
|
||||
)
|
||||
|
||||
inputcapture_intf.Release(
|
||||
self.current_session_handle, dbus.Dictionary(options, signature="sv")
|
||||
)
|
||||
|
||||
method_calls = mock_intf.GetMethodCalls("Release")
|
||||
assert len(method_calls) > 0
|
||||
_, args = method_calls[-1]
|
||||
assert args[0] == self.current_session_handle
|
||||
assert "activation_id" in args[2]
|
||||
aid = args[2]["activation_id"]
|
||||
assert aid == activation_id
|
||||
if cursor_position:
|
||||
assert "cursor_position" in args[2]
|
||||
pos = args[2]["cursor_position"]
|
||||
assert pos == cursor_position
|
||||
|
||||
def test_version(self, portals, dbus_con):
|
||||
xdp.check_version(dbus_con, "InputCapture", 1)
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"template_params",
|
||||
(
|
||||
{
|
||||
"inputcapture": {
|
||||
"supported_capabilities": 0b101, # KEYBOARD, POINTER, TOUCH
|
||||
},
|
||||
},
|
||||
),
|
||||
)
|
||||
def test_supported_capabilities(self, portals, dbus_con):
|
||||
properties_intf = xdp.get_iface(dbus_con, "org.freedesktop.DBus.Properties")
|
||||
|
||||
caps = properties_intf.Get(
|
||||
"org.freedesktop.portal.InputCapture", "SupportedCapabilities"
|
||||
)
|
||||
assert caps == 0b101
|
||||
|
||||
def test_create_session(self, portals, dbus_con):
|
||||
mock_intf = xdp.get_mock_iface(dbus_con)
|
||||
|
||||
self.create_session(dbus_con, capabilities=0b1) # KEYBOARD
|
||||
|
||||
# Check the impl portal was called with the right args
|
||||
method_calls = mock_intf.GetMethodCalls("CreateSession")
|
||||
assert len(method_calls) == 1
|
||||
_, args = method_calls.pop(0)
|
||||
assert args[3] == "" # parent window
|
||||
assert args[4]["capabilities"] == 0b1
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"template_params",
|
||||
(
|
||||
{
|
||||
"inputcapture": {
|
||||
"capabilities": 0b110, # TOUCH, POINTER
|
||||
"supported_capabilities": 0b111, # TOUCH, POINTER, KEYBOARD
|
||||
},
|
||||
},
|
||||
),
|
||||
)
|
||||
def test_create_session_limited_caps(self, portals, dbus_con):
|
||||
mock_intf = xdp.get_mock_iface(dbus_con)
|
||||
|
||||
# Request more caps than are supported
|
||||
response, results = self.create_session(dbus_con, capabilities=0b111)
|
||||
caps = results["capabilities"]
|
||||
# Returned capabilities must the ones we set up in the params
|
||||
assert caps == 0b110
|
||||
|
||||
# Check the impl portal was called with the right args
|
||||
method_calls = mock_intf.GetMethodCalls("CreateSession")
|
||||
assert len(method_calls) == 1
|
||||
_, args = method_calls.pop(0)
|
||||
assert args[3] == "" # parent window
|
||||
assert args[4]["capabilities"] == 0b111
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"template_params",
|
||||
(
|
||||
{
|
||||
"inputcapture": {
|
||||
"default-zone": dbus.Array(
|
||||
[dbus.Struct(z, signature="uuii") for z in default_zones()],
|
||||
signature="(uuii)",
|
||||
variant_level=1,
|
||||
)
|
||||
},
|
||||
},
|
||||
),
|
||||
)
|
||||
def test_get_zones(self, portals, dbus_con, zones):
|
||||
mock_intf = xdp.get_mock_iface(dbus_con)
|
||||
|
||||
response, results = self.create_session(dbus_con)
|
||||
response, results = self.get_zones(dbus_con)
|
||||
for z1, z2 in zip(results["zones"], zones):
|
||||
assert z1 == z2
|
||||
|
||||
# Check the impl portal was called with the right args
|
||||
method_calls = mock_intf.GetMethodCalls("CreateSession")
|
||||
assert len(method_calls) == 1
|
||||
method_calls = mock_intf.GetMethodCalls("GetZones")
|
||||
assert len(method_calls) == 1
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"template_params",
|
||||
(
|
||||
{
|
||||
"inputcapture": {
|
||||
"default-zone": dbus.Array(
|
||||
[dbus.Struct(z, signature="uuii") for z in default_zones()],
|
||||
signature="(uuii)",
|
||||
variant_level=1,
|
||||
)
|
||||
},
|
||||
},
|
||||
),
|
||||
)
|
||||
def test_set_pointer_barriers(self, portals, dbus_con, zones):
|
||||
mock_intf = xdp.get_mock_iface(dbus_con)
|
||||
|
||||
response, results = self.create_session(dbus_con)
|
||||
response, results = self.get_zones(dbus_con)
|
||||
|
||||
barriers = [
|
||||
{
|
||||
"barrier_id": dbus.UInt32(10, variant_level=1),
|
||||
"position": dbus.Struct(
|
||||
[0, 0, 0, 768], signature="iiii", variant_level=1
|
||||
),
|
||||
},
|
||||
{
|
||||
"barrier_id": dbus.UInt32(11, variant_level=1),
|
||||
"position": dbus.Struct(
|
||||
[0, 0, 1024, 0], signature="iiii", variant_level=1
|
||||
),
|
||||
},
|
||||
{
|
||||
"barrier_id": dbus.UInt32(12, variant_level=1),
|
||||
"position": dbus.Struct(
|
||||
[1024, 0, 1024, 768], signature="iiii", variant_level=1
|
||||
),
|
||||
},
|
||||
{
|
||||
"barrier_id": dbus.UInt32(13, variant_level=1),
|
||||
"position": dbus.Struct(
|
||||
[0, 768, 1024, 768], signature="iiii", variant_level=1
|
||||
),
|
||||
},
|
||||
{
|
||||
"barrier_id": dbus.UInt32(14, variant_level=1),
|
||||
"position": dbus.Struct(
|
||||
[100, 768, 500, 768], signature="iiii", variant_level=1
|
||||
),
|
||||
},
|
||||
{
|
||||
"barrier_id": dbus.UInt32(15, variant_level=1),
|
||||
"position": dbus.Struct(
|
||||
[1024, 0, 1024, 480], signature="iiii", variant_level=1
|
||||
),
|
||||
},
|
||||
{
|
||||
"barrier_id": dbus.UInt32(16, variant_level=1),
|
||||
"position": dbus.Struct(
|
||||
[1024 + 640, 0, 1024 + 640, 480], signature="iiii", variant_level=1
|
||||
),
|
||||
},
|
||||
# invalid ones
|
||||
{
|
||||
"barrier_id": dbus.UInt32(20, variant_level=1),
|
||||
"position": dbus.Struct(
|
||||
[0, 1, 3, 4], signature="iiii", variant_level=1
|
||||
),
|
||||
},
|
||||
{
|
||||
"barrier_id": dbus.UInt32(21, variant_level=1),
|
||||
"position": dbus.Struct(
|
||||
[0, 1, 1024, 1], signature="iiii", variant_level=1
|
||||
),
|
||||
},
|
||||
{
|
||||
"barrier_id": dbus.UInt32(22, variant_level=1),
|
||||
"position": dbus.Struct(
|
||||
[1, 0, 1, 768], signature="iiii", variant_level=1
|
||||
),
|
||||
},
|
||||
{
|
||||
"barrier_id": dbus.UInt32(23, variant_level=1),
|
||||
"position": dbus.Struct(
|
||||
[1023, 0, 1023, 768], signature="iiii", variant_level=1
|
||||
),
|
||||
},
|
||||
{
|
||||
"barrier_id": dbus.UInt32(24, variant_level=1),
|
||||
"position": dbus.Struct(
|
||||
[0, 0, 1050, 0], signature="iiii", variant_level=1
|
||||
),
|
||||
},
|
||||
]
|
||||
response, results = self.set_pointer_barriers(dbus_con, barriers=barriers)
|
||||
failed_barriers = results["failed_barriers"]
|
||||
assert all([id >= 20 for id in failed_barriers])
|
||||
|
||||
for id in [b["barrier_id"] for b in barriers if b["barrier_id"] >= 20]:
|
||||
assert id in failed_barriers
|
||||
|
||||
# Check the impl portal was called with the right args
|
||||
method_calls = mock_intf.GetMethodCalls("CreateSession")
|
||||
assert len(method_calls) == 1
|
||||
method_calls = mock_intf.GetMethodCalls("GetZones")
|
||||
assert len(method_calls) == 1
|
||||
method_calls = mock_intf.GetMethodCalls("SetPointerBarriers")
|
||||
assert len(method_calls) == 1
|
||||
_, args = method_calls.pop(0)
|
||||
assert args[4] == barriers
|
||||
assert args[5] == self.current_zone_set
|
||||
|
||||
def test_connect_to_eis(self, portals, dbus_con):
|
||||
self.create_session(dbus_con)
|
||||
self.get_zones(dbus_con)
|
||||
|
||||
# The default zone is 1920x1080
|
||||
barriers = [
|
||||
{
|
||||
"barrier_id": dbus.UInt32(10, variant_level=1),
|
||||
"position": dbus.Struct(
|
||||
[0, 0, 1920, 0], signature="iiii", variant_level=1
|
||||
),
|
||||
},
|
||||
]
|
||||
self.set_pointer_barriers(dbus_con, barriers)
|
||||
|
||||
self.connect_to_eis(dbus_con)
|
||||
|
||||
def test_enable_disable(self, portals, dbus_con):
|
||||
mock_intf = xdp.get_mock_iface(dbus_con)
|
||||
|
||||
self.create_session(dbus_con)
|
||||
self.get_zones(dbus_con)
|
||||
|
||||
# The default zone is 1920x1080
|
||||
barriers = [
|
||||
{
|
||||
"barrier_id": dbus.UInt32(10, variant_level=1),
|
||||
"position": dbus.Struct(
|
||||
[0, 0, 1920, 0], signature="iiii", variant_level=1
|
||||
),
|
||||
},
|
||||
]
|
||||
self.set_pointer_barriers(dbus_con, barriers)
|
||||
self.connect_to_eis(dbus_con)
|
||||
|
||||
# Disable before enable should be a noop
|
||||
self.disable(dbus_con)
|
||||
|
||||
method_calls = mock_intf.GetMethodCalls("Disable")
|
||||
assert len(method_calls) == 1
|
||||
|
||||
self.enable(dbus_con)
|
||||
method_calls = mock_intf.GetMethodCalls("Enable")
|
||||
assert len(method_calls) == 1
|
||||
|
||||
self.disable(dbus_con)
|
||||
method_calls = mock_intf.GetMethodCalls("Disable")
|
||||
assert len(method_calls) == 2
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"template_params",
|
||||
(
|
||||
{
|
||||
"inputcapture": {
|
||||
"disable-delay": 200,
|
||||
},
|
||||
},
|
||||
),
|
||||
)
|
||||
def test_disable_signal(self, portals, dbus_con):
|
||||
inputcapture_intf = xdp.get_portal_iface(dbus_con, "InputCapture")
|
||||
|
||||
self.create_session(dbus_con)
|
||||
self.get_zones(dbus_con)
|
||||
# The default zone is 1920x1080
|
||||
barriers = [
|
||||
{
|
||||
"barrier_id": dbus.UInt32(10, variant_level=1),
|
||||
"position": dbus.Struct(
|
||||
[0, 0, 1920, 0], signature="iiii", variant_level=1
|
||||
),
|
||||
},
|
||||
]
|
||||
self.set_pointer_barriers(dbus_con, barriers)
|
||||
self.connect_to_eis(dbus_con)
|
||||
|
||||
disabled_signal_received = False
|
||||
|
||||
def cb_disabled(session_handle, options):
|
||||
nonlocal disabled_signal_received
|
||||
disabled_signal_received = True
|
||||
assert session_handle == session_handle
|
||||
|
||||
inputcapture_intf.connect_to_signal("Disabled", cb_disabled)
|
||||
self.enable(dbus_con)
|
||||
xdp.wait_for(lambda: disabled_signal_received)
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"template_params",
|
||||
(
|
||||
{
|
||||
"inputcapture": {
|
||||
"activated-delay": 200,
|
||||
"deactivated-delay": 300,
|
||||
},
|
||||
},
|
||||
),
|
||||
)
|
||||
def test_activated_signal(self, portals, dbus_con):
|
||||
inputcapture_intf = xdp.get_portal_iface(dbus_con, "InputCapture")
|
||||
|
||||
self.create_session(dbus_con)
|
||||
self.get_zones(dbus_con)
|
||||
# The default zone is 1920x1080
|
||||
barriers = [
|
||||
{
|
||||
"barrier_id": dbus.UInt32(10, variant_level=1),
|
||||
"position": dbus.Struct(
|
||||
[0, 0, 1920, 0], signature="iiii", variant_level=1
|
||||
),
|
||||
},
|
||||
]
|
||||
self.set_pointer_barriers(dbus_con, barriers)
|
||||
self.connect_to_eis(dbus_con)
|
||||
|
||||
disabled_signal_received = False
|
||||
activated_signal_received = False
|
||||
deactivated_signal_received = False
|
||||
|
||||
def cb_disabled(session_handle, options):
|
||||
nonlocal disabled_signal_received
|
||||
disabled_signal_received = True
|
||||
|
||||
def cb_activated(session_handle, options):
|
||||
nonlocal activated_signal_received
|
||||
activated_signal_received = True
|
||||
assert session_handle == session_handle
|
||||
assert "activation_id" in options
|
||||
assert "barrier_id" in options
|
||||
assert options["barrier_id"] == 10 # template uses first barrier
|
||||
assert "cursor_position" in options
|
||||
assert options["cursor_position"] == (
|
||||
10.0,
|
||||
20.0,
|
||||
) # template uses x+10, y+20 of first barrier
|
||||
|
||||
def cb_deactivated(session_handle, options):
|
||||
nonlocal deactivated_signal_received
|
||||
deactivated_signal_received = True
|
||||
assert session_handle == session_handle
|
||||
assert "activation_id" in options
|
||||
assert "cursor_position" in options
|
||||
assert options["cursor_position"] == (
|
||||
10.0,
|
||||
20.0,
|
||||
) # template uses x+10, y+20 of first barrier
|
||||
|
||||
inputcapture_intf.connect_to_signal("Activated", cb_activated)
|
||||
inputcapture_intf.connect_to_signal("Deactivated", cb_deactivated)
|
||||
inputcapture_intf.connect_to_signal("Disabled", cb_disabled)
|
||||
|
||||
self.enable(dbus_con)
|
||||
|
||||
xdp.wait_for(lambda: activated_signal_received and deactivated_signal_received)
|
||||
assert not disabled_signal_received
|
||||
|
||||
# Disabling should not trigger the signal
|
||||
self.disable(dbus_con)
|
||||
assert not disabled_signal_received
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"template_params",
|
||||
(
|
||||
{
|
||||
"inputcapture": {
|
||||
"zones-changed-delay": 200,
|
||||
},
|
||||
},
|
||||
),
|
||||
)
|
||||
def test_zones_changed_signal(self, portals, dbus_con):
|
||||
inputcapture_intf = xdp.get_portal_iface(dbus_con, "InputCapture")
|
||||
|
||||
self.create_session(dbus_con)
|
||||
self.get_zones(dbus_con)
|
||||
# The default zone is 1920x1080
|
||||
barriers = [
|
||||
{
|
||||
"barrier_id": dbus.UInt32(10, variant_level=1),
|
||||
"position": dbus.Struct(
|
||||
[0, 0, 1920, 0], signature="iiii", variant_level=1
|
||||
),
|
||||
},
|
||||
]
|
||||
self.set_pointer_barriers(dbus_con, barriers)
|
||||
self.connect_to_eis(dbus_con)
|
||||
|
||||
zones_changed_signal_received = False
|
||||
|
||||
def cb_zones_changed(session_handle, options):
|
||||
nonlocal zones_changed_signal_received
|
||||
zones_changed_signal_received = True
|
||||
assert session_handle == session_handle
|
||||
|
||||
inputcapture_intf.connect_to_signal("ZonesChanged", cb_zones_changed)
|
||||
self.enable(dbus_con)
|
||||
xdp.wait_for(lambda: zones_changed_signal_received)
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"template_params",
|
||||
(
|
||||
{
|
||||
"inputcapture": {
|
||||
"activated-delay": 200,
|
||||
"deactivated-delay": 1000,
|
||||
"disabled-delay": 1200,
|
||||
},
|
||||
},
|
||||
),
|
||||
)
|
||||
def test_release(self, portals, dbus_con):
|
||||
inputcapture_intf = xdp.get_portal_iface(dbus_con, "InputCapture")
|
||||
|
||||
self.create_session(dbus_con)
|
||||
self.get_zones(dbus_con)
|
||||
# The default zone is 1920x1080
|
||||
barriers = [
|
||||
{
|
||||
"barrier_id": dbus.UInt32(10, variant_level=1),
|
||||
"position": dbus.Struct(
|
||||
[0, 0, 1920, 0], signature="iiii", variant_level=1
|
||||
),
|
||||
},
|
||||
]
|
||||
self.set_pointer_barriers(dbus_con, barriers)
|
||||
self.connect_to_eis(dbus_con)
|
||||
|
||||
disabled_signal_received = False
|
||||
activated_signal_received = False
|
||||
deactivated_signal_received = False
|
||||
activation_id = None
|
||||
|
||||
def cb_disabled(session_handle, options):
|
||||
nonlocal disabled_signal_received
|
||||
disabled_signal_received = True
|
||||
|
||||
def cb_activated(session_handle, options):
|
||||
nonlocal activated_signal_received, activation_id
|
||||
activated_signal_received = True
|
||||
activation_id = options["activation_id"]
|
||||
|
||||
def cb_deactivated(session_handle, options):
|
||||
nonlocal deactivated_signal_received
|
||||
deactivated_signal_received = True
|
||||
|
||||
inputcapture_intf.connect_to_signal("Disabled", cb_activated)
|
||||
inputcapture_intf.connect_to_signal("Activated", cb_activated)
|
||||
inputcapture_intf.connect_to_signal("Deactivated", cb_deactivated)
|
||||
|
||||
self.enable(dbus_con)
|
||||
|
||||
xdp.wait_for(lambda: activated_signal_received)
|
||||
assert activation_id is not None
|
||||
assert not deactivated_signal_received
|
||||
assert not disabled_signal_received
|
||||
|
||||
self.release(
|
||||
dbus_con, cursor_position=(10.0, 50.0), activation_id=activation_id
|
||||
)
|
||||
|
||||
# XDP should filter any signals the implementation may
|
||||
# send after Release().
|
||||
|
||||
mainloop = GLib.MainLoop()
|
||||
GLib.timeout_add(1000, mainloop.quit)
|
||||
mainloop.run()
|
||||
|
||||
# Release() implies deactivated
|
||||
assert not deactivated_signal_received
|
||||
assert not disabled_signal_received
|
||||
125
Telegram/ThirdParty/xdg-desktop-portal/tests/test_location.py
vendored
Normal file
125
Telegram/ThirdParty/xdg-desktop-portal/tests/test_location.py
vendored
Normal file
@@ -0,0 +1,125 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
import tests as xdp
|
||||
|
||||
import pytest
|
||||
import dbus
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def required_templates():
|
||||
return {"geoclue2": {}}
|
||||
|
||||
|
||||
class TestLocation:
|
||||
def test_version(self, portals, dbus_con):
|
||||
xdp.check_version(dbus_con, "Location", 1)
|
||||
|
||||
def get_geoclue_mock(self, dbus_con_sys):
|
||||
geoclue_manager_proxy = dbus_con_sys.get_object(
|
||||
"org.freedesktop.GeoClue2",
|
||||
"/org/freedesktop/GeoClue2/Manager",
|
||||
)
|
||||
geoclue_manager = dbus.Interface(
|
||||
geoclue_manager_proxy, "org.freedesktop.GeoClue2.Manager"
|
||||
)
|
||||
geoclue_client_proxy = dbus_con_sys.get_object(
|
||||
"org.freedesktop.GeoClue2", geoclue_manager.GetClient()
|
||||
)
|
||||
geoclue_mock = dbus.Interface(
|
||||
geoclue_client_proxy, "org.freedesktop.GeoClue2.Mock"
|
||||
)
|
||||
return geoclue_mock
|
||||
|
||||
@pytest.mark.parametrize("required_templates", ({},))
|
||||
def test_no_geoclue(self, portals, dbus_con):
|
||||
location_intf = xdp.get_portal_iface(dbus_con, "Location")
|
||||
|
||||
session = xdp.Session(
|
||||
dbus_con,
|
||||
location_intf.CreateSession({"session_handle_token": "session_token0"}),
|
||||
)
|
||||
|
||||
start_session_request = xdp.Request(dbus_con, location_intf)
|
||||
start_session_response = start_session_request.call(
|
||||
"Start",
|
||||
session_handle=session.handle,
|
||||
parent_window="window-hndl",
|
||||
options={},
|
||||
)
|
||||
|
||||
assert start_session_response
|
||||
assert start_session_response.response == 2
|
||||
|
||||
def test_session_update(self, portals, dbus_con, dbus_con_sys):
|
||||
location_intf = xdp.get_portal_iface(dbus_con, "Location")
|
||||
geoclue_mock_intf = self.get_geoclue_mock(dbus_con_sys)
|
||||
|
||||
location_updated = False
|
||||
updated_count = 0
|
||||
|
||||
session = xdp.Session(
|
||||
dbus_con,
|
||||
location_intf.CreateSession({"session_handle_token": "session_token0"}),
|
||||
)
|
||||
|
||||
def cb_location_updated(session_handle, location):
|
||||
nonlocal location_updated
|
||||
nonlocal updated_count
|
||||
|
||||
if updated_count == 0:
|
||||
assert location["Latitude"] == 0
|
||||
assert location["Longitude"] == 0
|
||||
assert location["Accuracy"] == 0
|
||||
elif updated_count == 1:
|
||||
assert location["Latitude"] == 11
|
||||
assert location["Longitude"] == 22
|
||||
assert location["Accuracy"] == 3
|
||||
|
||||
updated_count += 1
|
||||
location_updated = True
|
||||
|
||||
location_intf.connect_to_signal("LocationUpdated", cb_location_updated)
|
||||
|
||||
start_session_request = xdp.Request(dbus_con, location_intf)
|
||||
start_session_response = start_session_request.call(
|
||||
"Start",
|
||||
session_handle=session.handle,
|
||||
parent_window="window-hndl",
|
||||
options={},
|
||||
)
|
||||
|
||||
assert start_session_response
|
||||
assert start_session_response.response == 0
|
||||
|
||||
xdp.wait_for(lambda: location_updated)
|
||||
location_updated = False
|
||||
|
||||
assert updated_count == 1
|
||||
|
||||
geoclue_mock_intf.ChangeLocation(
|
||||
{
|
||||
"Latitude": dbus.UInt32(11),
|
||||
"Longitude": dbus.UInt32(22),
|
||||
"Accuracy": dbus.UInt32(3),
|
||||
}
|
||||
)
|
||||
|
||||
xdp.wait_for(lambda: location_updated)
|
||||
location_updated = False
|
||||
|
||||
assert updated_count == 2
|
||||
|
||||
def test_bad_accuracy(self, portals, dbus_con):
|
||||
location_intf = xdp.get_portal_iface(dbus_con, "Location")
|
||||
with pytest.raises(dbus.exceptions.DBusException) as excinfo:
|
||||
location_intf.CreateSession(
|
||||
{
|
||||
"session_handle_token": "session_token0",
|
||||
"accuracy": dbus.UInt32(22),
|
||||
}
|
||||
)
|
||||
assert (
|
||||
excinfo.value.get_dbus_name()
|
||||
== "org.freedesktop.portal.Error.InvalidArgument"
|
||||
)
|
||||
531
Telegram/ThirdParty/xdg-desktop-portal/tests/test_notification.py
vendored
Normal file
531
Telegram/ThirdParty/xdg-desktop-portal/tests/test_notification.py
vendored
Normal file
@@ -0,0 +1,531 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
#
|
||||
# This file is formatted with Python Black
|
||||
|
||||
import tests as xdp
|
||||
|
||||
import pytest
|
||||
import tempfile
|
||||
import os
|
||||
from pathlib import Path
|
||||
from gi.repository import GLib, Gio
|
||||
|
||||
SVG_IMAGE_DATA = """<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="16px" width="16px"/>
|
||||
"""
|
||||
|
||||
SOUND_DATA = (
|
||||
b"\x52\x49\x46\x46\x24\x00\x00\x00\x57\x41\x56\x45" +
|
||||
b"\x66\x6d\x74\x20\x10\x00\x00\x00\x01\x00\x01\x00" +
|
||||
b"\x44\xac\x00\x00\x88\x58\x01\x00\x02\x00\x10\x00" +
|
||||
b"\x64\x61\x74\x61\x00\x00\x00\x00"
|
||||
) # fmt: skip
|
||||
|
||||
|
||||
SUPPORTED_OPTIONS = {
|
||||
"foo": "bar",
|
||||
}
|
||||
|
||||
NOTIFICATION_BASIC = {
|
||||
"title": GLib.Variant("s", "title"),
|
||||
"body": GLib.Variant("s", "test notification body"),
|
||||
"priority": GLib.Variant("s", "normal"),
|
||||
"default-action": GLib.Variant("s", "test-action"),
|
||||
}
|
||||
|
||||
NOTIFICATION_BUTTONS = {
|
||||
"title": GLib.Variant("s", "test notification 2"),
|
||||
"body": GLib.Variant("s", "test notification body 2"),
|
||||
"priority": GLib.Variant("s", "low"),
|
||||
"default-action": GLib.Variant("s", "test-action"),
|
||||
"buttons": GLib.Variant(
|
||||
"aa{sv}",
|
||||
[
|
||||
{
|
||||
"label": GLib.Variant("s", "button1"),
|
||||
"action": GLib.Variant("s", "action1"),
|
||||
},
|
||||
{
|
||||
"label": GLib.Variant("s", "button2"),
|
||||
"action": GLib.Variant("s", "action2"),
|
||||
},
|
||||
],
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def required_templates():
|
||||
return {
|
||||
"notification": {
|
||||
"SupportedOptions": SUPPORTED_OPTIONS,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
class NotificationPortal(xdp.GDBusIface):
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
"org.freedesktop.portal.Desktop",
|
||||
"/org/freedesktop/portal/desktop",
|
||||
"org.freedesktop.portal.Notification",
|
||||
)
|
||||
|
||||
def AddNotification(self, id, notification, fds=[]):
|
||||
return self._call(
|
||||
"AddNotification",
|
||||
GLib.Variant("(sa{sv})", (id, notification)),
|
||||
fds,
|
||||
)
|
||||
|
||||
def RemoveNotification(self, id):
|
||||
return self._call(
|
||||
"RemoveNotification",
|
||||
GLib.Variant("(s)", (id,)),
|
||||
)
|
||||
|
||||
|
||||
class TestNotification:
|
||||
def check_notification(
|
||||
self, dbus_con, app_id, id, notification_in, notification_expected
|
||||
):
|
||||
notification_intf = NotificationPortal()
|
||||
mock_intf = xdp.get_mock_iface(dbus_con)
|
||||
|
||||
method_calls = mock_intf.GetMethodCalls("AddNotification")
|
||||
backend_calls = len(method_calls)
|
||||
|
||||
notification_intf.AddNotification(id, notification_in)
|
||||
|
||||
# Check the impl portal was called with the right args
|
||||
method_calls = mock_intf.GetMethodCalls("AddNotification")
|
||||
assert len(method_calls) == backend_calls + 1
|
||||
_, args = method_calls[-1]
|
||||
assert args[0] == app_id
|
||||
assert args[1] == id
|
||||
|
||||
mock_notification = args[2]
|
||||
assert (
|
||||
mock_notification == GLib.Variant("a{sv}", notification_expected).unpack()
|
||||
)
|
||||
|
||||
def test_version(self, portals, dbus_con):
|
||||
xdp.check_version(dbus_con, "Notification", 2)
|
||||
|
||||
def test_basic(self, portals, dbus_con, app_id):
|
||||
self.check_notification(
|
||||
dbus_con,
|
||||
app_id,
|
||||
"test1",
|
||||
NOTIFICATION_BASIC,
|
||||
NOTIFICATION_BASIC,
|
||||
)
|
||||
|
||||
def test_remove(self, portals, dbus_con, app_id):
|
||||
notification_intf = NotificationPortal()
|
||||
mock_intf = xdp.get_mock_iface(dbus_con)
|
||||
|
||||
id = "test1"
|
||||
|
||||
notification_intf.AddNotification(id, NOTIFICATION_BASIC)
|
||||
method_calls = mock_intf.GetMethodCalls("AddNotification")
|
||||
assert len(method_calls) == 1
|
||||
_, args = method_calls[-1]
|
||||
assert args[0] == app_id
|
||||
assert args[1] == id
|
||||
|
||||
notification_intf.RemoveNotification(id)
|
||||
method_calls = mock_intf.GetMethodCalls("RemoveNotification")
|
||||
assert len(method_calls) == 1
|
||||
_, args = method_calls[-1]
|
||||
assert args[0] == app_id
|
||||
assert args[1] == id
|
||||
|
||||
def test_buttons(self, portals, dbus_con, app_id):
|
||||
self.check_notification(
|
||||
dbus_con,
|
||||
app_id,
|
||||
"test1",
|
||||
NOTIFICATION_BUTTONS,
|
||||
NOTIFICATION_BUTTONS,
|
||||
)
|
||||
|
||||
def test_markup(self, portals, dbus_con, app_id):
|
||||
bodies = [
|
||||
(
|
||||
"test <b>notification</b> body <i>italic</i>",
|
||||
"test <b>notification</b> body <i>italic</i>",
|
||||
),
|
||||
(
|
||||
'test <a href="https://example.com"><b>Some link</b></a>',
|
||||
'test <a href="https://example.com"><b>Some link</b></a>',
|
||||
),
|
||||
(
|
||||
"<html>",
|
||||
"<html>",
|
||||
),
|
||||
(
|
||||
'<a href="https://xkcd.com/327/#"><html>"></a>',
|
||||
'<a href="https://xkcd.com/327/#"><html>"></a>',
|
||||
),
|
||||
(
|
||||
"test \n newline \n\n some more space \n with trailing space ",
|
||||
"test newline some more space with trailing space",
|
||||
),
|
||||
(
|
||||
"test <custom> tag </custom>",
|
||||
"test tag",
|
||||
),
|
||||
(
|
||||
"test <b>notification<b> body",
|
||||
False,
|
||||
),
|
||||
(
|
||||
"<b>foo<i>bar</b></i>",
|
||||
False,
|
||||
),
|
||||
(
|
||||
"test <markup><i>notification</i><markup> body",
|
||||
False,
|
||||
),
|
||||
]
|
||||
|
||||
i = 0
|
||||
for body_in, body_expected in bodies:
|
||||
notification_in = NOTIFICATION_BASIC.copy()
|
||||
notification_in["markup-body"] = GLib.Variant("s", body_in)
|
||||
|
||||
notification_expected = NOTIFICATION_BASIC.copy()
|
||||
if body_expected:
|
||||
notification_expected["markup-body"] = GLib.Variant("s", body_expected)
|
||||
|
||||
try:
|
||||
self.check_notification(
|
||||
dbus_con,
|
||||
app_id,
|
||||
f"test{i}",
|
||||
notification_in,
|
||||
notification_expected,
|
||||
)
|
||||
assert body_expected
|
||||
except GLib.GError as e:
|
||||
assert "invalid markup-body" in e.message
|
||||
|
||||
i += 1
|
||||
|
||||
def test_bad_arg(self, portals, dbus_con, app_id):
|
||||
notification = NOTIFICATION_BASIC.copy()
|
||||
notification["bodx"] = GLib.Variant("s", "Xtest")
|
||||
|
||||
self.check_notification(
|
||||
dbus_con,
|
||||
app_id,
|
||||
"test1",
|
||||
notification,
|
||||
NOTIFICATION_BASIC,
|
||||
)
|
||||
|
||||
def test_bad_priority(self, portals, dbus_con, app_id):
|
||||
notification = NOTIFICATION_BASIC.copy()
|
||||
notification["priority"] = GLib.Variant("s", "invalid")
|
||||
|
||||
try:
|
||||
self.check_notification(
|
||||
dbus_con,
|
||||
app_id,
|
||||
"test1",
|
||||
notification,
|
||||
notification,
|
||||
)
|
||||
assert False, "This statement should not be reached"
|
||||
except GLib.GError as e:
|
||||
assert "invalid not a priority" in e.message
|
||||
|
||||
def test_bad_button(self, portals, dbus_con, app_id):
|
||||
notification = NOTIFICATION_BUTTONS.copy()
|
||||
notification["buttons"] = GLib.Variant(
|
||||
"aa{sv}",
|
||||
[
|
||||
{
|
||||
"labex": GLib.Variant("s", "button1"),
|
||||
"action": GLib.Variant("s", "action1"),
|
||||
},
|
||||
],
|
||||
)
|
||||
|
||||
try:
|
||||
self.check_notification(
|
||||
dbus_con,
|
||||
app_id,
|
||||
"test1",
|
||||
notification,
|
||||
notification,
|
||||
)
|
||||
assert False, "This statement should not be reached"
|
||||
except GLib.GError as e:
|
||||
assert "invalid button" in e.message
|
||||
|
||||
def test_display_hint(self, portals, dbus_con, app_id):
|
||||
notification = NOTIFICATION_BASIC.copy()
|
||||
notification["display-hint"] = GLib.Variant(
|
||||
"as",
|
||||
[
|
||||
"transient",
|
||||
"show-as-new",
|
||||
],
|
||||
)
|
||||
|
||||
self.check_notification(
|
||||
dbus_con,
|
||||
app_id,
|
||||
"test1",
|
||||
notification,
|
||||
notification,
|
||||
)
|
||||
|
||||
notification = NOTIFICATION_BASIC.copy()
|
||||
notification["display-hint"] = GLib.Variant(
|
||||
"as",
|
||||
[
|
||||
"unsupported-hint",
|
||||
],
|
||||
)
|
||||
|
||||
try:
|
||||
self.check_notification(
|
||||
dbus_con,
|
||||
app_id,
|
||||
"test1",
|
||||
notification,
|
||||
notification,
|
||||
)
|
||||
assert False, "This statement should not be reached"
|
||||
except GLib.GError as e:
|
||||
assert "not a display-hint" in e.message
|
||||
|
||||
def test_category(self, portals, dbus_con, app_id):
|
||||
notification = NOTIFICATION_BASIC.copy()
|
||||
notification["category"] = GLib.Variant("s", "im.received")
|
||||
|
||||
self.check_notification(
|
||||
dbus_con,
|
||||
app_id,
|
||||
"test1",
|
||||
notification,
|
||||
notification,
|
||||
)
|
||||
|
||||
notification = NOTIFICATION_BASIC.copy()
|
||||
notification["category"] = GLib.Variant("s", "x-vendor.custom")
|
||||
|
||||
self.check_notification(
|
||||
dbus_con,
|
||||
app_id,
|
||||
"test1",
|
||||
notification,
|
||||
notification,
|
||||
)
|
||||
|
||||
notification = NOTIFICATION_BASIC.copy()
|
||||
notification["category"] = GLib.Variant("s", "unsupported-type")
|
||||
|
||||
try:
|
||||
self.check_notification(
|
||||
dbus_con,
|
||||
app_id,
|
||||
"test1",
|
||||
notification,
|
||||
notification,
|
||||
)
|
||||
assert False, "This statement should not be reached"
|
||||
except GLib.GError as e:
|
||||
assert "not a supported category" in e.message
|
||||
|
||||
def test_supported_options(self, portals, dbus_con, app_id):
|
||||
properties_intf = xdp.get_iface(dbus_con, "org.freedesktop.DBus.Properties")
|
||||
|
||||
options = properties_intf.Get(
|
||||
"org.freedesktop.portal.Notification", "SupportedOptions"
|
||||
)
|
||||
|
||||
assert options == SUPPORTED_OPTIONS
|
||||
|
||||
def test_icon_themed(self, portals, dbus_con, app_id):
|
||||
notification_intf = NotificationPortal()
|
||||
icon = Gio.ThemedIcon.new("test-icon-symbolic")
|
||||
|
||||
notification = NOTIFICATION_BASIC.copy()
|
||||
notification["icon"] = icon.serialize()
|
||||
|
||||
notification_intf.AddNotification("test1", notification)
|
||||
|
||||
def test_icon_bytes(self, portals, dbus_con, app_id):
|
||||
notification_intf = NotificationPortal()
|
||||
bytes = GLib.Bytes.new(SVG_IMAGE_DATA.encode("utf-8"))
|
||||
icon = Gio.BytesIcon.new(bytes)
|
||||
|
||||
notification = NOTIFICATION_BASIC.copy()
|
||||
notification["icon"] = icon.serialize()
|
||||
|
||||
notification_intf.AddNotification("test1", notification)
|
||||
|
||||
def test_icon_file(self, portals, dbus_con, app_id):
|
||||
notification_intf = NotificationPortal()
|
||||
fd, file_path = tempfile.mkstemp(prefix="notification_icon_", dir=Path.home())
|
||||
os.write(fd, SVG_IMAGE_DATA.encode("utf-8"))
|
||||
|
||||
file = Gio.File.new_for_path(file_path)
|
||||
icon = Gio.FileIcon.new(file)
|
||||
|
||||
notification = NOTIFICATION_BASIC.copy()
|
||||
notification["icon"] = icon.serialize()
|
||||
|
||||
notification = {
|
||||
"title": GLib.Variant("s", "title"),
|
||||
"icon": icon.serialize(),
|
||||
}
|
||||
|
||||
notification_intf.AddNotification("test1", notification)
|
||||
|
||||
def test_icon_bad(self, portals, dbus_con, app_id):
|
||||
notification_intf = NotificationPortal()
|
||||
|
||||
notification = NOTIFICATION_BASIC.copy()
|
||||
|
||||
bad_icons = [
|
||||
GLib.Variant("(sv)", ["themed", GLib.Variant("s", "test-icon-symbolic")]),
|
||||
GLib.Variant(
|
||||
"(sv)",
|
||||
["bytes", GLib.Variant("as", ["test-icon-symbolic", "test-icon"])],
|
||||
),
|
||||
GLib.Variant("(sv)", ["file-descriptor", GLib.Variant("s", "")]),
|
||||
GLib.Variant("(sv)", ["file-descriptor", GLib.Variant("h", 0)]),
|
||||
]
|
||||
|
||||
for icon in bad_icons:
|
||||
notification["icon"] = icon
|
||||
try:
|
||||
notification_intf.AddNotification("test1", notification)
|
||||
assert False, "This statement should not be reached"
|
||||
except GLib.GError as e:
|
||||
assert e.matches(Gio.io_error_quark(), Gio.IOErrorEnum.DBUS_ERROR)
|
||||
|
||||
def test_sound_simple(self, portals, dbus_con, app_id):
|
||||
notification = NOTIFICATION_BASIC.copy()
|
||||
notification["sound"] = GLib.Variant("s", "default")
|
||||
|
||||
self.check_notification(
|
||||
dbus_con,
|
||||
app_id,
|
||||
"test1",
|
||||
notification,
|
||||
notification,
|
||||
)
|
||||
|
||||
notification = NOTIFICATION_BASIC.copy()
|
||||
notification["sound"] = GLib.Variant("s", "silent")
|
||||
|
||||
self.check_notification(
|
||||
dbus_con,
|
||||
app_id,
|
||||
"test1",
|
||||
notification,
|
||||
notification,
|
||||
)
|
||||
|
||||
notification = NOTIFICATION_BASIC.copy()
|
||||
notification["sound"] = GLib.Variant("s", "bad")
|
||||
|
||||
try:
|
||||
self.check_notification(
|
||||
dbus_con,
|
||||
app_id,
|
||||
"test1",
|
||||
notification,
|
||||
notification,
|
||||
)
|
||||
assert False, "This statement should not be reached"
|
||||
except GLib.GError as e:
|
||||
assert "invalid sound: invalid option" in e.message
|
||||
|
||||
def test_sound_file(self, portals, dbus_con, app_id):
|
||||
notification_intf = NotificationPortal()
|
||||
mock_intf = xdp.get_mock_iface(dbus_con)
|
||||
|
||||
fd, file_path = tempfile.mkstemp(prefix="notification_sound_", dir=Path.home())
|
||||
os.write(fd, SOUND_DATA)
|
||||
|
||||
file = Gio.File.new_for_path(file_path)
|
||||
|
||||
notification = NOTIFICATION_BASIC.copy()
|
||||
notification["sound"] = GLib.Variant(
|
||||
"(sv)",
|
||||
(
|
||||
"file",
|
||||
GLib.Variant("s", file.get_uri()),
|
||||
),
|
||||
)
|
||||
|
||||
notification_intf.AddNotification("test1", notification)
|
||||
|
||||
method_calls = mock_intf.GetMethodCalls("AddNotification")
|
||||
assert len(method_calls) == 1
|
||||
_, args = method_calls[-1]
|
||||
mock_notification = args[2]
|
||||
|
||||
assert "sound" not in mock_notification
|
||||
|
||||
def test_sound_fd(self, portals, dbus_con, app_id):
|
||||
notification_intf = NotificationPortal()
|
||||
mock_intf = xdp.get_mock_iface(dbus_con)
|
||||
|
||||
fd = os.memfd_create("notification_sound_test", os.MFD_ALLOW_SEALING)
|
||||
os.write(fd, SOUND_DATA)
|
||||
|
||||
notification = NOTIFICATION_BASIC.copy()
|
||||
notification["sound"] = GLib.Variant(
|
||||
"(sv)",
|
||||
(
|
||||
"file-descriptor",
|
||||
GLib.Variant("h", 0),
|
||||
),
|
||||
)
|
||||
|
||||
notification_intf.AddNotification("test1", notification, [fd])
|
||||
|
||||
method_calls = mock_intf.GetMethodCalls("AddNotification")
|
||||
assert len(method_calls) == 1
|
||||
_, args = method_calls[-1]
|
||||
mock_notification = args[2]
|
||||
|
||||
assert mock_notification["sound"][0] == "file-descriptor"
|
||||
mock_fd = mock_notification["sound"][1]
|
||||
mock_fd = mock_fd.take()
|
||||
|
||||
os.lseek(fd, 0, os.SEEK_SET)
|
||||
fd_contents = os.read(mock_fd, 1000)
|
||||
assert fd_contents == SOUND_DATA
|
||||
|
||||
os.close(mock_fd)
|
||||
os.close(fd)
|
||||
|
||||
def test_sound_bad(self, portals, dbus_con, app_id):
|
||||
notification_intf = NotificationPortal()
|
||||
|
||||
notification = NOTIFICATION_BASIC.copy()
|
||||
|
||||
bad_sounds = [
|
||||
# bad type
|
||||
GLib.Variant("(sv)", ["file-descriptor", GLib.Variant("s", "")]),
|
||||
# not sending the FD for the handle
|
||||
GLib.Variant("(sv)", ["file-descriptor", GLib.Variant("h", 13)]),
|
||||
]
|
||||
|
||||
for sound in bad_sounds:
|
||||
notification["sound"] = sound
|
||||
try:
|
||||
notification_intf.AddNotification("test1", notification)
|
||||
assert False, "This statement should not be reached"
|
||||
except GLib.GError as e:
|
||||
assert e.matches(Gio.io_error_quark(), Gio.IOErrorEnum.DBUS_ERROR)
|
||||
pass
|
||||
352
Telegram/ThirdParty/xdg-desktop-portal/tests/test_openuri.py
vendored
Normal file
352
Telegram/ThirdParty/xdg-desktop-portal/tests/test_openuri.py
vendored
Normal file
@@ -0,0 +1,352 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
#
|
||||
# This file is formatted with Python Black
|
||||
|
||||
import tests as xdp
|
||||
|
||||
import dbus
|
||||
import pytest
|
||||
import os
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
|
||||
defaults_list = b"""[Default Applications]
|
||||
x-scheme-handler/http=furrfix.desktop;
|
||||
text/plain=furrfix.desktop
|
||||
"""
|
||||
|
||||
furrfix_desktop = b"""[Desktop Entry]
|
||||
Version=1.0
|
||||
Name=Furrfix
|
||||
GenericName=Not a Web Browser
|
||||
Comment=Don't Browse the Web
|
||||
Exec=true %u
|
||||
Icon=furrfix
|
||||
Terminal=false
|
||||
Type=Application
|
||||
MimeType=text/plain;text/html;text/xml;application/xhtml+xml;application/vnd.mozilla.xul+xml;text/mml;x-scheme-handler/http;x-scheme-handler/https;x-scheme-handler/xdg-desktop-portal-test;
|
||||
StartupNotify=true
|
||||
Categories=Network;WebBrowser;
|
||||
Keywords=web;browser;internet;
|
||||
"""
|
||||
|
||||
mimeinfo_cache = b"""[MIME Cache]
|
||||
application/vnd.mozilla.xul+xml=furrfix.desktop;
|
||||
application/xhtml+xml=furrfix.desktop;
|
||||
text/plain=furrfix.desktop;
|
||||
text/html=furrfix.desktop;
|
||||
text/mml=furrfix.desktop;
|
||||
text/xml=furrfix.desktop;
|
||||
x-scheme-handler/http=furrfix.desktop;
|
||||
x-scheme-handler/https=furrfix.desktop;
|
||||
x-scheme-handler/xdg-desktop-portal-test=furrfix.desktop;
|
||||
"""
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def xdg_data_home_files():
|
||||
return {
|
||||
"applications/defaults.list": defaults_list,
|
||||
"applications/furrfix.desktop": furrfix_desktop,
|
||||
"applications/mimeinfo.cache": mimeinfo_cache,
|
||||
}
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def required_templates():
|
||||
return {
|
||||
"appchooser": {},
|
||||
"lockdown": {},
|
||||
}
|
||||
|
||||
|
||||
class TestOpenURI:
|
||||
def set_permissions(self, dbus_con, type, permissions):
|
||||
perm_store_intf = xdp.get_permission_store_iface(dbus_con)
|
||||
perm_store_intf.SetPermission(
|
||||
"desktop-used-apps",
|
||||
True,
|
||||
"inhibit",
|
||||
type,
|
||||
permissions,
|
||||
)
|
||||
|
||||
def enable_paranoid_mode(self, dbus_con, type):
|
||||
# turn on paranoid mode to ensure we get a backend call
|
||||
perm_store_intf = xdp.get_permission_store_iface(dbus_con)
|
||||
perm_store_intf.SetValue(
|
||||
"desktop-used-apps",
|
||||
True,
|
||||
type,
|
||||
dbus.Dictionary(
|
||||
{
|
||||
"always-ask": True,
|
||||
},
|
||||
signature="sv",
|
||||
),
|
||||
)
|
||||
|
||||
def test_version(self, portals, dbus_con):
|
||||
xdp.check_version(dbus_con, "OpenURI", 5)
|
||||
|
||||
def test_http1(self, portals, dbus_con, app_id):
|
||||
openuri_intf = xdp.get_portal_iface(dbus_con, "OpenURI")
|
||||
mock_intf = xdp.get_mock_iface(dbus_con)
|
||||
|
||||
scheme_handler = "x-scheme-handler/http"
|
||||
self.enable_paranoid_mode(dbus_con, scheme_handler)
|
||||
|
||||
uri = "http://www.flatpak.org"
|
||||
writable = False
|
||||
activation_token = "token"
|
||||
|
||||
request = xdp.Request(dbus_con, openuri_intf)
|
||||
options = {
|
||||
"writable": writable,
|
||||
"activation_token": activation_token,
|
||||
}
|
||||
response = request.call(
|
||||
"OpenURI",
|
||||
parent_window="",
|
||||
uri=uri,
|
||||
options=options,
|
||||
)
|
||||
|
||||
assert response
|
||||
assert response.response == 0
|
||||
|
||||
# Check the impl portal was called with the right args
|
||||
method_calls = mock_intf.GetMethodCalls("ChooseApplication")
|
||||
assert len(method_calls) > 0
|
||||
_, args = method_calls[-1]
|
||||
assert args[1] == app_id
|
||||
assert args[2] == "" # parent window
|
||||
assert "furrfix" in args[3]
|
||||
assert args[4]["uri"] == uri
|
||||
assert args[4]["content_type"] == scheme_handler
|
||||
assert args[4]["activation_token"] == activation_token
|
||||
|
||||
def test_http2(self, portals, dbus_con):
|
||||
openuri_intf = xdp.get_portal_iface(dbus_con, "OpenURI")
|
||||
mock_intf = xdp.get_mock_iface(dbus_con)
|
||||
|
||||
scheme_handler = "x-scheme-handler/http"
|
||||
self.set_permissions(dbus_con, scheme_handler, ["furrfix", "3", "3"])
|
||||
|
||||
uri = "http://www.flatpak.org"
|
||||
writable = False
|
||||
activation_token = "token"
|
||||
|
||||
request = xdp.Request(dbus_con, openuri_intf)
|
||||
options = {
|
||||
"writable": writable,
|
||||
"activation_token": activation_token,
|
||||
}
|
||||
response = request.call(
|
||||
"OpenURI",
|
||||
parent_window="",
|
||||
uri=uri,
|
||||
options=options,
|
||||
)
|
||||
|
||||
assert response
|
||||
assert response.response == 0
|
||||
|
||||
# Check the impl portal was not called because the choice thresold
|
||||
# has been reached
|
||||
method_calls = mock_intf.GetMethodCalls("ChooseApplication")
|
||||
assert len(method_calls) == 0
|
||||
|
||||
def test_file(self, portals, dbus_con, app_id):
|
||||
openuri_intf = xdp.get_portal_iface(dbus_con, "OpenURI")
|
||||
mock_intf = xdp.get_mock_iface(dbus_con)
|
||||
|
||||
scheme_handler = "text/plain"
|
||||
self.enable_paranoid_mode(dbus_con, scheme_handler)
|
||||
|
||||
fd, _ = tempfile.mkstemp(prefix="openuri_mock_file_", dir=Path.home())
|
||||
os.write(fd, b"openuri_mock_file")
|
||||
|
||||
writable = False
|
||||
activation_token = "token"
|
||||
|
||||
request = xdp.Request(dbus_con, openuri_intf)
|
||||
options = {
|
||||
"writable": writable,
|
||||
"activation_token": activation_token,
|
||||
}
|
||||
response = request.call(
|
||||
"OpenFile",
|
||||
parent_window="",
|
||||
fd=fd,
|
||||
options=options,
|
||||
)
|
||||
|
||||
assert response
|
||||
assert response.response == 0
|
||||
|
||||
# Check the impl portal was called with the right args
|
||||
method_calls = mock_intf.GetMethodCalls("ChooseApplication")
|
||||
assert len(method_calls) > 0
|
||||
_, args = method_calls[-1]
|
||||
assert args[1] == app_id
|
||||
assert args[2] == "" # parent window
|
||||
assert "furrfix" in args[3]
|
||||
assert args[4]["content_type"] == scheme_handler
|
||||
assert args[4]["activation_token"] == activation_token
|
||||
|
||||
path = args[4]["uri"]
|
||||
assert path.startswith("file:///")
|
||||
|
||||
with open(path[7:]) as file:
|
||||
openuri_file_contents = file.read()
|
||||
assert openuri_file_contents == "openuri_mock_file"
|
||||
|
||||
@pytest.mark.parametrize("template_params", ({"appchooser": {"response": 1}},))
|
||||
def test_cancel(self, portals, dbus_con):
|
||||
openuri_intf = xdp.get_portal_iface(dbus_con, "OpenURI")
|
||||
|
||||
scheme_handler = "x-scheme-handler/http"
|
||||
self.enable_paranoid_mode(dbus_con, scheme_handler)
|
||||
|
||||
uri = "http://www.flatpak.org"
|
||||
|
||||
request = xdp.Request(dbus_con, openuri_intf)
|
||||
options: Any = {}
|
||||
response = request.call(
|
||||
"OpenURI",
|
||||
parent_window="",
|
||||
uri=uri,
|
||||
options=options,
|
||||
)
|
||||
|
||||
assert response
|
||||
assert response.response == 1
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"template_params", ({"appchooser": {"expect-close": True}},)
|
||||
)
|
||||
def test_close(self, portals, dbus_con, app_id):
|
||||
openuri_intf = xdp.get_portal_iface(dbus_con, "OpenURI")
|
||||
mock_intf = xdp.get_mock_iface(dbus_con)
|
||||
|
||||
scheme_handler = "x-scheme-handler/http"
|
||||
self.enable_paranoid_mode(dbus_con, scheme_handler)
|
||||
|
||||
uri = "http://www.flatpak.org"
|
||||
writable = False
|
||||
activation_token = "token"
|
||||
|
||||
request = xdp.Request(dbus_con, openuri_intf)
|
||||
request.schedule_close(1000)
|
||||
options = {
|
||||
"writable": writable,
|
||||
"activation_token": activation_token,
|
||||
}
|
||||
request.call(
|
||||
"OpenURI",
|
||||
parent_window="",
|
||||
uri=uri,
|
||||
options=options,
|
||||
)
|
||||
|
||||
# Only true if the impl.Request was closed too
|
||||
assert request.closed
|
||||
|
||||
# Check the impl portal was called with the right args
|
||||
method_calls = mock_intf.GetMethodCalls("ChooseApplication")
|
||||
assert len(method_calls) > 0
|
||||
_, args = method_calls[-1]
|
||||
assert args[1] == app_id
|
||||
assert args[2] == "" # parent window
|
||||
assert "furrfix" in args[3]
|
||||
assert args[4]["uri"] == uri
|
||||
assert args[4]["content_type"] == scheme_handler
|
||||
assert args[4]["activation_token"] == activation_token
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"template_params", ({"lockdown": {"disable-application-handlers": True}},)
|
||||
)
|
||||
def test_lockdown(self, portals, dbus_con, app_id):
|
||||
openuri_intf = xdp.get_portal_iface(dbus_con, "OpenURI")
|
||||
|
||||
scheme_handler = "x-scheme-handler/http"
|
||||
self.enable_paranoid_mode(dbus_con, scheme_handler)
|
||||
|
||||
uri = "http://www.flatpak.org"
|
||||
writable = False
|
||||
activation_token = "token"
|
||||
|
||||
request = xdp.Request(dbus_con, openuri_intf)
|
||||
options = {
|
||||
"writable": writable,
|
||||
"activation_token": activation_token,
|
||||
}
|
||||
with pytest.raises(dbus.exceptions.DBusException) as excinfo:
|
||||
request.call(
|
||||
"OpenURI",
|
||||
parent_window="",
|
||||
uri=uri,
|
||||
options=options,
|
||||
)
|
||||
assert (
|
||||
excinfo.value.get_dbus_name() == "org.freedesktop.portal.Error.NotAllowed"
|
||||
)
|
||||
|
||||
def test_dir(self, portals, dbus_con, app_id):
|
||||
openuri_intf = xdp.get_portal_iface(dbus_con, "OpenURI")
|
||||
mock_intf = xdp.get_mock_iface(dbus_con)
|
||||
|
||||
scheme_handler = "inode/directory"
|
||||
self.enable_paranoid_mode(dbus_con, scheme_handler)
|
||||
|
||||
fd, file_path = tempfile.mkstemp(prefix="openuri_mock_file_", dir=Path.home())
|
||||
os.write(fd, b"openuri_mock_file")
|
||||
|
||||
activation_token = "token"
|
||||
|
||||
request = xdp.Request(dbus_con, openuri_intf)
|
||||
options = {
|
||||
"activation_token": activation_token,
|
||||
}
|
||||
response = request.call(
|
||||
"OpenDirectory",
|
||||
parent_window="",
|
||||
fd=fd,
|
||||
options=options,
|
||||
)
|
||||
|
||||
assert response
|
||||
assert response.response == 0
|
||||
|
||||
# Check the appchooser portal got called to open the containing dir
|
||||
method_calls = mock_intf.GetMethodCalls("ChooseApplication")
|
||||
assert len(method_calls) > 0
|
||||
_, args = method_calls[-1]
|
||||
assert args[1] == app_id
|
||||
assert args[2] == "" # parent window
|
||||
assert args[4]["content_type"] == scheme_handler
|
||||
assert args[4]["activation_token"] == activation_token
|
||||
|
||||
path = args[4]["uri"]
|
||||
assert path.startswith("file:///")
|
||||
|
||||
assert Path(path[7:]) == Path(file_path).parent
|
||||
|
||||
def test_scheme_supported(self, portals, dbus_con):
|
||||
openuri_intf = xdp.get_portal_iface(dbus_con, "OpenURI")
|
||||
|
||||
supported = openuri_intf.SchemeSupported("https", {})
|
||||
assert supported
|
||||
|
||||
supported = openuri_intf.SchemeSupported("bogusnonexistanthandler", {})
|
||||
assert not supported
|
||||
|
||||
with pytest.raises(dbus.exceptions.DBusException) as excinfo:
|
||||
openuri_intf.SchemeSupported("", {})
|
||||
assert (
|
||||
excinfo.value.get_dbus_name()
|
||||
== "org.freedesktop.portal.Error.InvalidArgument"
|
||||
)
|
||||
298
Telegram/ThirdParty/xdg-desktop-portal/tests/test_permission_store.py
vendored
Normal file
298
Telegram/ThirdParty/xdg-desktop-portal/tests/test_permission_store.py
vendored
Normal file
@@ -0,0 +1,298 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
#
|
||||
# This file is formatted with Python Black
|
||||
|
||||
import tests as xdp
|
||||
|
||||
import dbus
|
||||
from gi.repository import GLib, Gio
|
||||
|
||||
|
||||
class PermissionStore(xdp.GDBusIface):
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
"org.freedesktop.impl.portal.PermissionStore",
|
||||
"/org/freedesktop/impl/portal/PermissionStore",
|
||||
"org.freedesktop.impl.portal.PermissionStore",
|
||||
)
|
||||
|
||||
def Lookup(self, table, id):
|
||||
return self._call(
|
||||
"Lookup",
|
||||
GLib.Variant("(ss)", (table, id)),
|
||||
)
|
||||
|
||||
def Set(self, table, create, id, perm, data):
|
||||
return self._call(
|
||||
"Set",
|
||||
GLib.Variant("(sbsa{sas}v)", (table, create, id, perm, data)),
|
||||
)
|
||||
|
||||
def SetValue(self, table, create, id, data):
|
||||
return self._call(
|
||||
"SetValue",
|
||||
GLib.Variant("(sbsv)", (table, create, id, data)),
|
||||
)
|
||||
|
||||
def SetPermission(self, table, create, id, app, perm):
|
||||
return self._call(
|
||||
"SetPermission",
|
||||
GLib.Variant("(sbssas)", (table, create, id, app, perm)),
|
||||
)
|
||||
|
||||
def SetPermissionAsync(self, table, create, id, app, perm, user_cb):
|
||||
self._call_async(
|
||||
"SetPermission",
|
||||
GLib.Variant("(sbssas)", (table, create, id, app, perm)),
|
||||
cb=user_cb,
|
||||
)
|
||||
|
||||
def DeletePermissionAsync(self, table, id, app, user_cb):
|
||||
self._call_async(
|
||||
"DeletePermission",
|
||||
GLib.Variant("(sss)", (table, id, app)),
|
||||
cb=user_cb,
|
||||
)
|
||||
|
||||
def DeleteAsync(self, table, id, user_cb):
|
||||
self._call_async(
|
||||
"Delete",
|
||||
GLib.Variant("(ss)", (table, id)),
|
||||
cb=user_cb,
|
||||
)
|
||||
|
||||
def Delete(self, table, id):
|
||||
return self._call(
|
||||
"Delete",
|
||||
GLib.Variant("(ss)", (table, id)),
|
||||
)
|
||||
|
||||
def GetPermission(self, table, id, app):
|
||||
return self._call(
|
||||
"GetPermission",
|
||||
GLib.Variant("(sss)", (table, id, app)),
|
||||
)
|
||||
|
||||
|
||||
class TestPermissionStore:
|
||||
def test_version(self, portals, dbus_con):
|
||||
permission_store = dbus_con.get_object(
|
||||
"org.freedesktop.impl.portal.PermissionStore",
|
||||
"/org/freedesktop/impl/portal/PermissionStore",
|
||||
)
|
||||
|
||||
properties_intf = dbus.Interface(
|
||||
permission_store,
|
||||
"org.freedesktop.DBus.Properties",
|
||||
)
|
||||
portal_version = properties_intf.Get(
|
||||
"org.freedesktop.impl.portal.PermissionStore",
|
||||
"version",
|
||||
)
|
||||
assert int(portal_version) == 2
|
||||
|
||||
def test_delete_race(self, portals, dbus_con):
|
||||
permission_store_intf = PermissionStore()
|
||||
finished_count = 0
|
||||
|
||||
table = "inhibit"
|
||||
id = "inhibit"
|
||||
perms = ["logout", "suspend"]
|
||||
|
||||
def cb(_):
|
||||
nonlocal finished_count
|
||||
|
||||
finished_count += 1
|
||||
|
||||
permission_store_intf.SetPermissionAsync(table, True, id, "a", perms, cb)
|
||||
permission_store_intf.DeleteAsync(table, id, cb)
|
||||
|
||||
xdp.wait_for(lambda: finished_count >= 2)
|
||||
|
||||
try:
|
||||
permission_store_intf.Lookup(table, id)
|
||||
assert False, "This statement should not be reached"
|
||||
except GLib.GError as e:
|
||||
assert "org.freedesktop.portal.Error.NotFound" in e.message
|
||||
assert e.matches(Gio.io_error_quark(), Gio.IOErrorEnum.DBUS_ERROR)
|
||||
|
||||
permission_store_intf.SetPermissionAsync(table, True, id, "a", perms, cb)
|
||||
permission_store_intf.SetPermissionAsync(table, True, id, "b", perms, cb)
|
||||
permission_store_intf.DeletePermissionAsync(table, id, "a", cb)
|
||||
|
||||
xdp.wait_for(lambda: finished_count >= 4)
|
||||
|
||||
result, _ = permission_store_intf.Lookup(table, id)
|
||||
perms_out = result.unpack()[0]
|
||||
assert perms_out == {"b": perms}
|
||||
|
||||
permission_store_intf.SetPermissionAsync(table, True, id, "a", perms, cb)
|
||||
permission_store_intf.DeletePermissionAsync(table, id, "b", cb)
|
||||
permission_store_intf.DeletePermissionAsync(table, id, "a", cb)
|
||||
|
||||
xdp.wait_for(lambda: finished_count >= 7)
|
||||
|
||||
result, _ = permission_store_intf.Lookup(table, id)
|
||||
perms_out = result.unpack()[0]
|
||||
assert perms_out == {}
|
||||
|
||||
def test_change(self, portals, dbus_con):
|
||||
permission_store_intf = PermissionStore()
|
||||
changed_count = 0
|
||||
|
||||
table = "TEST"
|
||||
id = "test-resource"
|
||||
app = "one.two.three"
|
||||
perms = ["one", "two"]
|
||||
|
||||
def cb_changed1(results):
|
||||
nonlocal changed_count
|
||||
|
||||
cb_table, cb_id, deleted, _, cb_perms = results.unpack()
|
||||
|
||||
assert cb_table == table
|
||||
assert cb_id == id
|
||||
assert not deleted
|
||||
assert cb_perms[app] == perms
|
||||
|
||||
changed_count += 1
|
||||
|
||||
cs = permission_store_intf.connect_to_signal("Changed", cb_changed1)
|
||||
permission_store_intf.SetPermissionAsync(table, True, id, app, perms, None)
|
||||
xdp.wait_for(lambda: changed_count >= 1)
|
||||
cs.disconnect()
|
||||
|
||||
def cb_changed2(results):
|
||||
nonlocal changed_count
|
||||
|
||||
cb_table, cb_id, deleted, _, _ = results.unpack()
|
||||
|
||||
assert cb_table == table
|
||||
assert cb_id == id
|
||||
assert deleted
|
||||
|
||||
changed_count += 1
|
||||
|
||||
cs = permission_store_intf.connect_to_signal("Changed", cb_changed2)
|
||||
permission_store_intf.Delete(table, id)
|
||||
xdp.wait_for(lambda: changed_count >= 2)
|
||||
cs.disconnect()
|
||||
|
||||
def test_lookup(self, portals, dbus_con):
|
||||
permission_store_intf = PermissionStore()
|
||||
|
||||
table = "TEST"
|
||||
id = "test-resource"
|
||||
perms = ["one", "two"]
|
||||
data = True
|
||||
|
||||
try:
|
||||
permission_store_intf.Lookup(table, id)
|
||||
assert False, "This statement should not be reached"
|
||||
except GLib.GError as e:
|
||||
assert "org.freedesktop.portal.Error.NotFound" in e.message
|
||||
|
||||
permissions = [(id, perms)]
|
||||
permission_store_intf.Set(table, True, id, permissions, GLib.Variant("b", data))
|
||||
|
||||
result, _ = permission_store_intf.Lookup(table, id)
|
||||
perms_out = result.unpack()[0]
|
||||
data_out = result.unpack()[1]
|
||||
|
||||
assert id in perms_out
|
||||
perms_out = perms_out[id]
|
||||
assert perms_out == perms
|
||||
|
||||
assert data_out == data
|
||||
|
||||
def test_set_value(self, portals, dbus_con):
|
||||
permission_store_intf = PermissionStore()
|
||||
|
||||
table = "TEST"
|
||||
id = "test-resource"
|
||||
data = True
|
||||
|
||||
try:
|
||||
permission_store_intf.Lookup(table, id)
|
||||
assert False, "This statement should not be reached"
|
||||
except GLib.GError as e:
|
||||
assert "org.freedesktop.portal.Error.NotFound" in e.message
|
||||
|
||||
permission_store_intf.SetValue(table, True, id, GLib.Variant("b", data))
|
||||
|
||||
result, _ = permission_store_intf.Lookup(table, id)
|
||||
perms_out = result.unpack()[0]
|
||||
data_out = result.unpack()[1]
|
||||
assert perms_out == {}
|
||||
assert data_out == data
|
||||
|
||||
def test_create(self, portals, dbus_con):
|
||||
permission_store_intf = PermissionStore()
|
||||
|
||||
table = "inhibit"
|
||||
id = "inhibit"
|
||||
app = ""
|
||||
perms = ["logout", "suspend"]
|
||||
|
||||
try:
|
||||
permission_store_intf.SetPermission(
|
||||
table,
|
||||
# Do not create if it does not exist
|
||||
False,
|
||||
id,
|
||||
app,
|
||||
perms,
|
||||
)
|
||||
assert False, "This statement should not be reached"
|
||||
except GLib.GError as e:
|
||||
assert "org.freedesktop.portal.Error.NotFound" in e.message
|
||||
|
||||
permission_store_intf.SetPermission(table, True, id, app, perms)
|
||||
|
||||
def test_delete(self, portals, dbus_con):
|
||||
permission_store_intf = PermissionStore()
|
||||
|
||||
table = "inhibit"
|
||||
id = "inhibit"
|
||||
app = ""
|
||||
perms = ["logout", "suspend"]
|
||||
|
||||
try:
|
||||
permission_store_intf.Delete(table, id)
|
||||
assert False, "This statement should not be reached"
|
||||
except GLib.GError as e:
|
||||
assert "org.freedesktop.portal.Error.NotFound" in e.message
|
||||
|
||||
permission_store_intf.SetPermission(table, True, id, app, perms)
|
||||
|
||||
permission_store_intf.Delete(table, id)
|
||||
|
||||
try:
|
||||
permission_store_intf.Lookup(table, id)
|
||||
assert False, "This statement should not be reached"
|
||||
except GLib.GError as e:
|
||||
assert "org.freedesktop.portal.Error.NotFound" in e.message
|
||||
|
||||
def test_get_permission(self, portals, dbus_con):
|
||||
permission_store_intf = PermissionStore()
|
||||
|
||||
table = "notifications"
|
||||
id = "notification"
|
||||
app = "a"
|
||||
perms = ["yes"]
|
||||
|
||||
try:
|
||||
permission_store_intf.GetPermission(table, id, app)
|
||||
assert False, "This statement should not be reached"
|
||||
except GLib.GError as e:
|
||||
assert "org.freedesktop.portal.Error.NotFound" in e.message
|
||||
|
||||
permission_store_intf.SetPermission(table, True, id, app, perms)
|
||||
|
||||
result, _ = permission_store_intf.GetPermission(table, id, app)
|
||||
permissions = result.unpack()[0]
|
||||
assert permissions == perms
|
||||
|
||||
result, _ = permission_store_intf.GetPermission(table, id, "no-such-app")
|
||||
permissions = result.unpack()[0]
|
||||
assert permissions == []
|
||||
345
Telegram/ThirdParty/xdg-desktop-portal/tests/test_print.py
vendored
Normal file
345
Telegram/ThirdParty/xdg-desktop-portal/tests/test_print.py
vendored
Normal file
@@ -0,0 +1,345 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
#
|
||||
# This file is formatted with Python Black
|
||||
|
||||
import tests as xdp
|
||||
|
||||
import dbus
|
||||
import pytest
|
||||
import os
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
|
||||
PRINT_PREPARE_DATA = {
|
||||
"token": dbus.UInt32(1337),
|
||||
}
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def required_templates():
|
||||
return {
|
||||
"print": {
|
||||
"prepare-results": PRINT_PREPARE_DATA,
|
||||
},
|
||||
"lockdown": {},
|
||||
}
|
||||
|
||||
|
||||
class TestPrint:
|
||||
def test_version(self, portals, dbus_con):
|
||||
xdp.check_version(dbus_con, "Print", 3)
|
||||
|
||||
def test_prepare_print_basic(self, portals, dbus_con, app_id):
|
||||
print_intf = xdp.get_portal_iface(dbus_con, "Print")
|
||||
mock_intf = xdp.get_mock_iface(dbus_con)
|
||||
|
||||
title = "Test Title"
|
||||
settings: Any = {}
|
||||
page_setup: Any = {}
|
||||
options = {
|
||||
"modal": True,
|
||||
"accept_label": "Accept",
|
||||
"supported_output_file_formats": ["pdf"],
|
||||
}
|
||||
|
||||
request = xdp.Request(dbus_con, print_intf)
|
||||
response = request.call(
|
||||
"PreparePrint",
|
||||
parent_window="",
|
||||
title=title,
|
||||
settings=settings,
|
||||
page_setup=page_setup,
|
||||
options=options,
|
||||
)
|
||||
|
||||
assert response
|
||||
assert response.response == 0
|
||||
|
||||
# Check the impl portal was called with the right args
|
||||
method_calls = mock_intf.GetMethodCalls("PreparePrint")
|
||||
assert len(method_calls) == 1
|
||||
_, args = method_calls.pop()
|
||||
assert args[1] == app_id
|
||||
assert args[2] == "" # parent window
|
||||
assert args[3] == title
|
||||
assert args[4] == settings
|
||||
assert args[5] == page_setup
|
||||
assert args[6]["modal"] == options["modal"]
|
||||
assert args[6]["accept_label"] == options["accept_label"]
|
||||
assert (
|
||||
args[6]["supported_output_file_formats"]
|
||||
== options["supported_output_file_formats"]
|
||||
)
|
||||
|
||||
@pytest.mark.parametrize("template_params", ({"print": {"response": 1}},))
|
||||
def test_prepare_print_cancel(self, portals, dbus_con, app_id):
|
||||
print_intf = xdp.get_portal_iface(dbus_con, "Print")
|
||||
mock_intf = xdp.get_mock_iface(dbus_con)
|
||||
|
||||
title = "Test Title"
|
||||
|
||||
request = xdp.Request(dbus_con, print_intf)
|
||||
response = request.call(
|
||||
"PreparePrint",
|
||||
parent_window="",
|
||||
title=title,
|
||||
settings={},
|
||||
page_setup={},
|
||||
options={},
|
||||
)
|
||||
|
||||
assert response
|
||||
assert response.response == 1
|
||||
|
||||
# Check the impl portal was called with the right args
|
||||
method_calls = mock_intf.GetMethodCalls("PreparePrint")
|
||||
assert len(method_calls) == 1
|
||||
_, args = method_calls.pop()
|
||||
assert args[1] == app_id
|
||||
assert args[2] == "" # parent window
|
||||
assert args[3] == title
|
||||
|
||||
@pytest.mark.parametrize("template_params", ({"print": {"expect-close": True}},))
|
||||
def test_prepare_print_close(self, portals, dbus_con, app_id):
|
||||
print_intf = xdp.get_portal_iface(dbus_con, "Print")
|
||||
mock_intf = xdp.get_mock_iface(dbus_con)
|
||||
|
||||
title = "Test Title"
|
||||
|
||||
request = xdp.Request(dbus_con, print_intf)
|
||||
request.schedule_close(1000)
|
||||
request.call(
|
||||
"PreparePrint",
|
||||
parent_window="",
|
||||
title=title,
|
||||
settings={},
|
||||
page_setup={},
|
||||
options={},
|
||||
)
|
||||
|
||||
# Only true if the impl.Request was closed too
|
||||
assert request.closed
|
||||
|
||||
# Check the impl portal was called with the right args
|
||||
method_calls = mock_intf.GetMethodCalls("PreparePrint")
|
||||
assert len(method_calls) == 1
|
||||
_, args = method_calls.pop()
|
||||
assert args[1] == app_id
|
||||
assert args[2] == "" # parent window
|
||||
assert args[3] == title
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"template_params", ({"lockdown": {"disable-printing": True}},)
|
||||
)
|
||||
def test_prepare_print_lockdown(self, portals, dbus_con, app_id):
|
||||
print_intf = xdp.get_portal_iface(dbus_con, "Print")
|
||||
mock_intf = xdp.get_mock_iface(dbus_con)
|
||||
|
||||
title = "Test Title"
|
||||
|
||||
request = xdp.Request(dbus_con, print_intf)
|
||||
with pytest.raises(dbus.exceptions.DBusException) as excinfo:
|
||||
request.call(
|
||||
"PreparePrint",
|
||||
parent_window="",
|
||||
title=title,
|
||||
settings={},
|
||||
page_setup={},
|
||||
options={},
|
||||
)
|
||||
assert (
|
||||
excinfo.value.get_dbus_name() == "org.freedesktop.portal.Error.NotAllowed"
|
||||
)
|
||||
|
||||
# Check the impl portal was not called
|
||||
method_calls = mock_intf.GetMethodCalls("PreparePrint")
|
||||
assert len(method_calls) == 0
|
||||
|
||||
def test_print_basic(self, portals, dbus_con, app_id):
|
||||
print_intf = xdp.get_portal_iface(dbus_con, "Print")
|
||||
mock_intf = xdp.get_mock_iface(dbus_con)
|
||||
|
||||
fd, file_path = tempfile.mkstemp(prefix="print_mock_file_", dir=Path.home())
|
||||
os.write(fd, b"print_mock_file")
|
||||
|
||||
title = "Test Title"
|
||||
options = {
|
||||
"modal": True,
|
||||
"token": "token",
|
||||
"supported_output_file_formats": ["svg"],
|
||||
}
|
||||
|
||||
request = xdp.Request(dbus_con, print_intf)
|
||||
response = request.call(
|
||||
"Print",
|
||||
parent_window="",
|
||||
title=title,
|
||||
fd=fd,
|
||||
options=options,
|
||||
)
|
||||
|
||||
assert response
|
||||
assert response.response == 0
|
||||
|
||||
# Check the impl portal was called with the right args
|
||||
method_calls = mock_intf.GetMethodCalls("Print")
|
||||
assert len(method_calls) == 1
|
||||
_, args = method_calls.pop()
|
||||
assert args[1] == app_id
|
||||
assert args[2] == "" # parent window
|
||||
assert args[3] == title
|
||||
assert args[5]["modal"] == options["modal"]
|
||||
assert (
|
||||
args[5]["supported_output_file_formats"]
|
||||
== options["supported_output_file_formats"]
|
||||
)
|
||||
|
||||
backend_fd = args[4].take()
|
||||
ino = os.stat(f"/proc/self/fd/{fd}").st_ino
|
||||
ino_backend = os.stat(f"/proc/self/fd/{backend_fd}").st_ino
|
||||
os.close(fd)
|
||||
assert ino == ino_backend
|
||||
|
||||
@pytest.mark.parametrize("template_params", ({"print": {"response": 1}},))
|
||||
def test_print_cancel(self, portals, dbus_con, app_id):
|
||||
print_intf = xdp.get_portal_iface(dbus_con, "Print")
|
||||
mock_intf = xdp.get_mock_iface(dbus_con)
|
||||
|
||||
fd, _ = tempfile.mkstemp(prefix="print_mock_file_", dir=Path.home())
|
||||
os.write(fd, b"print_mock_file")
|
||||
|
||||
title = "Test Title"
|
||||
|
||||
request = xdp.Request(dbus_con, print_intf)
|
||||
response = request.call(
|
||||
"Print",
|
||||
parent_window="",
|
||||
title=title,
|
||||
fd=fd,
|
||||
options={},
|
||||
)
|
||||
|
||||
assert response
|
||||
assert response.response == 1
|
||||
|
||||
# Check the impl portal was called with the right args
|
||||
method_calls = mock_intf.GetMethodCalls("Print")
|
||||
assert len(method_calls) == 1
|
||||
_, args = method_calls.pop()
|
||||
assert args[1] == app_id
|
||||
|
||||
@pytest.mark.parametrize("template_params", ({"print": {"expect-close": True}},))
|
||||
def test_print_close(self, portals, dbus_con, app_id):
|
||||
print_intf = xdp.get_portal_iface(dbus_con, "Print")
|
||||
mock_intf = xdp.get_mock_iface(dbus_con)
|
||||
|
||||
fd, _ = tempfile.mkstemp(prefix="print_mock_file_", dir=Path.home())
|
||||
os.write(fd, b"print_mock_file")
|
||||
|
||||
title = "Test Title"
|
||||
|
||||
request = xdp.Request(dbus_con, print_intf)
|
||||
request.schedule_close(1000)
|
||||
request.call(
|
||||
"Print",
|
||||
parent_window="",
|
||||
title=title,
|
||||
fd=fd,
|
||||
options={},
|
||||
)
|
||||
|
||||
# Only true if the impl.Request was closed too
|
||||
assert request.closed
|
||||
|
||||
# Check the impl portal was called with the right args
|
||||
method_calls = mock_intf.GetMethodCalls("Print")
|
||||
assert len(method_calls) == 1
|
||||
_, args = method_calls.pop()
|
||||
assert args[1] == app_id
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"template_params", ({"lockdown": {"disable-printing": True}},)
|
||||
)
|
||||
def test_print_lockdown(self, portals, dbus_con, app_id):
|
||||
print_intf = xdp.get_portal_iface(dbus_con, "Print")
|
||||
mock_intf = xdp.get_mock_iface(dbus_con)
|
||||
|
||||
fd, _ = tempfile.mkstemp(prefix="print_mock_file_", dir=Path.home())
|
||||
os.write(fd, b"print_mock_file")
|
||||
|
||||
title = "Test Title"
|
||||
|
||||
request = xdp.Request(dbus_con, print_intf)
|
||||
with pytest.raises(dbus.exceptions.DBusException) as excinfo:
|
||||
request.call(
|
||||
"Print",
|
||||
parent_window="",
|
||||
title=title,
|
||||
fd=fd,
|
||||
options={},
|
||||
)
|
||||
assert (
|
||||
excinfo.value.get_dbus_name() == "org.freedesktop.portal.Error.NotAllowed"
|
||||
)
|
||||
|
||||
# Check the impl portal was not called
|
||||
method_calls = mock_intf.GetMethodCalls("Print")
|
||||
assert len(method_calls) == 0
|
||||
|
||||
def test_print_prepare_and_print(self, portals, dbus_con, app_id):
|
||||
print_intf = xdp.get_portal_iface(dbus_con, "Print")
|
||||
mock_intf = xdp.get_mock_iface(dbus_con)
|
||||
|
||||
title = "Test Title"
|
||||
|
||||
fd, file_path = tempfile.mkstemp(prefix="print_mock_file_", dir=Path.home())
|
||||
os.write(fd, b"print_mock_file")
|
||||
|
||||
request = xdp.Request(dbus_con, print_intf)
|
||||
response = request.call(
|
||||
"PreparePrint",
|
||||
parent_window="",
|
||||
title=title,
|
||||
settings={},
|
||||
page_setup={},
|
||||
options={},
|
||||
)
|
||||
|
||||
assert response
|
||||
assert response.response == 0
|
||||
assert response.results["token"] == PRINT_PREPARE_DATA["token"]
|
||||
|
||||
# Check the impl portal was called with the right args
|
||||
method_calls = mock_intf.GetMethodCalls("PreparePrint")
|
||||
assert len(method_calls) == 1
|
||||
|
||||
options = {
|
||||
"token": response.results["token"],
|
||||
}
|
||||
|
||||
request = xdp.Request(dbus_con, print_intf)
|
||||
response = request.call(
|
||||
"Print",
|
||||
parent_window="",
|
||||
title=title,
|
||||
fd=fd,
|
||||
options=options,
|
||||
)
|
||||
|
||||
assert response
|
||||
assert response.response == 0
|
||||
|
||||
# Check the impl portal was called with the right args
|
||||
method_calls = mock_intf.GetMethodCalls("Print")
|
||||
assert len(method_calls) == 1
|
||||
_, args = method_calls.pop()
|
||||
|
||||
assert args[5]["token"] == PRINT_PREPARE_DATA["token"]
|
||||
|
||||
backend_fd = args[4].take()
|
||||
ino = os.stat(f"/proc/self/fd/{fd}").st_ino
|
||||
ino_backend = os.stat(f"/proc/self/fd/{backend_fd}").st_ino
|
||||
os.close(fd)
|
||||
assert ino == ino_backend
|
||||
142
Telegram/ThirdParty/xdg-desktop-portal/tests/test_registry.py
vendored
Normal file
142
Telegram/ThirdParty/xdg-desktop-portal/tests/test_registry.py
vendored
Normal file
@@ -0,0 +1,142 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
import tests as xdp
|
||||
|
||||
import dbus
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def app_id():
|
||||
return "org.example.WrongAppId"
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def required_templates():
|
||||
return {"remotedesktop": {}}
|
||||
|
||||
|
||||
class TestRegistry:
|
||||
def test_version(self, portals, dbus_con):
|
||||
documents = dbus_con.get_object(
|
||||
"org.freedesktop.portal.Desktop",
|
||||
"/org/freedesktop/portal/desktop",
|
||||
)
|
||||
|
||||
properties_intf = dbus.Interface(
|
||||
documents,
|
||||
"org.freedesktop.DBus.Properties",
|
||||
)
|
||||
portal_version = properties_intf.Get(
|
||||
"org.freedesktop.host.portal.Registry",
|
||||
"version",
|
||||
)
|
||||
assert int(portal_version) == 1
|
||||
|
||||
def create_dummy_session(self, dbus_con):
|
||||
remotedesktop_intf = xdp.get_portal_iface(dbus_con, "RemoteDesktop")
|
||||
request = xdp.Request(dbus_con, remotedesktop_intf)
|
||||
|
||||
session_counter_attr_name = "session_counter"
|
||||
session_counter = getattr(self, session_counter_attr_name, 0)
|
||||
setattr(self, session_counter_attr_name, session_counter + 1)
|
||||
|
||||
options = {
|
||||
"session_handle_token": f"session_token{session_counter}",
|
||||
}
|
||||
response = request.call(
|
||||
"CreateSession",
|
||||
options=options,
|
||||
)
|
||||
assert response
|
||||
assert response.response == 0
|
||||
|
||||
return xdp.Session.from_response(dbus_con, response)
|
||||
|
||||
def test_registerless(self, portals, dbus_con, app_id):
|
||||
mock_intf = xdp.get_mock_iface(dbus_con)
|
||||
|
||||
expected_app_id = app_id
|
||||
|
||||
session = self.create_dummy_session(dbus_con)
|
||||
|
||||
app_id = mock_intf.GetSessionAppId(session.handle)
|
||||
assert app_id == expected_app_id
|
||||
|
||||
def test_register(self, portals, dbus_con):
|
||||
registry_intf = xdp.get_portal_iface(dbus_con, "Registry", domain="host")
|
||||
mock_intf = xdp.get_mock_iface(dbus_con)
|
||||
|
||||
expected_app_id = "org.example.CorrectAppId"
|
||||
registry_intf.Register(expected_app_id, {})
|
||||
|
||||
session = self.create_dummy_session(dbus_con)
|
||||
|
||||
app_id = mock_intf.GetSessionAppId(session.handle)
|
||||
assert app_id == expected_app_id
|
||||
|
||||
def test_late_register(self, portals, dbus_con, app_id):
|
||||
registry_intf = xdp.get_portal_iface(dbus_con, "Registry", domain="host")
|
||||
mock_intf = xdp.get_mock_iface(dbus_con)
|
||||
|
||||
expected_app_id = app_id
|
||||
unexpected_app_id = "org.example.CorrectAppId"
|
||||
|
||||
session = self.create_dummy_session(dbus_con)
|
||||
|
||||
app_id = mock_intf.GetSessionAppId(session.handle)
|
||||
assert app_id == expected_app_id
|
||||
|
||||
with pytest.raises(dbus.exceptions.DBusException) as exc_info:
|
||||
registry_intf.Register(unexpected_app_id, {})
|
||||
exc_info.match(".*Connection already associated with an application ID.*")
|
||||
|
||||
new_session = self.create_dummy_session(dbus_con)
|
||||
|
||||
new_app_id = mock_intf.GetSessionAppId(new_session.handle)
|
||||
assert new_app_id == expected_app_id
|
||||
|
||||
def test_multiple_connections(self, portals, dbus_con, app_id):
|
||||
registry_intf = xdp.get_portal_iface(dbus_con, "Registry", domain="host")
|
||||
mock_intf = xdp.get_mock_iface(dbus_con)
|
||||
|
||||
expected_app_id = "org.example.CorrectAppId"
|
||||
unexpected_app_id = app_id
|
||||
|
||||
registry_intf.Register(expected_app_id, {})
|
||||
session = self.create_dummy_session(dbus_con)
|
||||
app_id = mock_intf.GetSessionAppId(session.handle)
|
||||
assert app_id == expected_app_id
|
||||
|
||||
dbus_con2 = dbus.bus.BusConnection(dbus.bus.BusConnection.TYPE_SESSION)
|
||||
dbus_con2.set_exit_on_disconnect(False)
|
||||
mock_intf2 = xdp.get_mock_iface(dbus_con2)
|
||||
session2 = self.create_dummy_session(dbus_con2)
|
||||
app_id2 = mock_intf2.GetSessionAppId(session2.handle)
|
||||
assert app_id2 == unexpected_app_id
|
||||
dbus_con2.close()
|
||||
|
||||
dbus_con3 = dbus.bus.BusConnection(dbus.bus.BusConnection.TYPE_SESSION)
|
||||
dbus_con3.set_exit_on_disconnect(False)
|
||||
mock_intf3 = xdp.get_mock_iface(dbus_con3)
|
||||
registry_intf3 = xdp.get_portal_iface(dbus_con3, "Registry", domain="host")
|
||||
registry_intf3.Register(expected_app_id, {})
|
||||
session3 = self.create_dummy_session(dbus_con3)
|
||||
app_id3 = mock_intf3.GetSessionAppId(session3.handle)
|
||||
assert app_id3 == expected_app_id
|
||||
dbus_con3.close()
|
||||
|
||||
def test_no_reregister(self, portals, dbus_con):
|
||||
registry_intf = xdp.get_portal_iface(dbus_con, "Registry", domain="host")
|
||||
mock_intf = xdp.get_mock_iface(dbus_con)
|
||||
|
||||
expected_app_id = "org.example.CorrectAppId"
|
||||
|
||||
registry_intf.Register(expected_app_id, {})
|
||||
session = self.create_dummy_session(dbus_con)
|
||||
app_id = mock_intf.GetSessionAppId(session.handle)
|
||||
assert app_id == expected_app_id
|
||||
|
||||
with pytest.raises(dbus.exceptions.DBusException) as exc_info:
|
||||
registry_intf.Register(expected_app_id, {})
|
||||
exc_info.match(".*Connection already associated with an application ID.*")
|
||||
251
Telegram/ThirdParty/xdg-desktop-portal/tests/test_remotedesktop.py
vendored
Normal file
251
Telegram/ThirdParty/xdg-desktop-portal/tests/test_remotedesktop.py
vendored
Normal file
@@ -0,0 +1,251 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
#
|
||||
# This file is formatted with Python Black
|
||||
|
||||
import tests as xdp
|
||||
|
||||
import dbus
|
||||
import pytest
|
||||
import socket
|
||||
from typing import List, Dict, Any
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def required_templates():
|
||||
return {"remotedesktop": {}}
|
||||
|
||||
|
||||
class TestRemoteDesktop:
|
||||
def test_version(self, portals, dbus_con):
|
||||
xdp.check_version(dbus_con, "RemoteDesktop", 2)
|
||||
|
||||
def test_create_close_session(self, portals, dbus_con):
|
||||
remotedesktop_intf = xdp.get_portal_iface(dbus_con, "RemoteDesktop")
|
||||
mock_intf = xdp.get_mock_iface(dbus_con)
|
||||
|
||||
request = xdp.Request(dbus_con, remotedesktop_intf)
|
||||
options = {
|
||||
"session_handle_token": "session_token0",
|
||||
}
|
||||
response = request.call(
|
||||
"CreateSession",
|
||||
options=options,
|
||||
)
|
||||
|
||||
assert response
|
||||
assert response.response == 0
|
||||
|
||||
session = xdp.Session.from_response(dbus_con, response)
|
||||
# Check the impl portal was called with the right args
|
||||
method_calls = mock_intf.GetMethodCalls("CreateSession")
|
||||
assert len(method_calls) > 0
|
||||
_, args = method_calls[-1]
|
||||
assert args[1] == session.handle
|
||||
# assert args[2] == "" # appid, not necessary empty
|
||||
|
||||
session.close()
|
||||
xdp.wait_for(lambda: session.closed)
|
||||
|
||||
@pytest.mark.parametrize("token", ("Invalid-Token&", "", "/foo"))
|
||||
def test_remote_desktop_create_session_invalid(self, portals, dbus_con, token):
|
||||
remotedesktop_intf = xdp.get_portal_iface(dbus_con, "RemoteDesktop")
|
||||
|
||||
request = xdp.Request(dbus_con, remotedesktop_intf)
|
||||
options = {"session_handle_token": token}
|
||||
|
||||
with pytest.raises(dbus.exceptions.DBusException) as excinfo:
|
||||
request.call("CreateSession", options=options)
|
||||
|
||||
e = excinfo.value
|
||||
assert e.get_dbus_name() == "org.freedesktop.portal.Error.InvalidArgument"
|
||||
assert "Invalid token" in e.get_dbus_message()
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"template_params", ({"remotedesktop": {"force-close": 500}},)
|
||||
)
|
||||
def test_create_session_signal_closed(self, portals, dbus_con):
|
||||
remotedesktop_intf = xdp.get_portal_iface(dbus_con, "RemoteDesktop")
|
||||
mock_intf = xdp.get_mock_iface(dbus_con)
|
||||
|
||||
request = xdp.Request(dbus_con, remotedesktop_intf)
|
||||
options = {
|
||||
"session_handle_token": "session_token0",
|
||||
}
|
||||
response = request.call(
|
||||
"CreateSession",
|
||||
options=options,
|
||||
)
|
||||
|
||||
assert response
|
||||
assert response.response == 0
|
||||
|
||||
session = xdp.Session.from_response(dbus_con, response)
|
||||
# Check the impl portal was called with the right args
|
||||
method_calls = mock_intf.GetMethodCalls("CreateSession")
|
||||
assert len(method_calls) > 0
|
||||
_, args = method_calls[-1]
|
||||
assert args[1] == session.handle
|
||||
# assert args[2] == "" # appid, not necessary empty
|
||||
|
||||
# Now expect the backend to close it
|
||||
xdp.wait_for(lambda: session.closed)
|
||||
|
||||
def test_connect_to_eis(self, portals, dbus_con):
|
||||
remotedesktop_intf = xdp.get_portal_iface(dbus_con, "RemoteDesktop")
|
||||
|
||||
request = xdp.Request(dbus_con, remotedesktop_intf)
|
||||
options = {
|
||||
"session_handle_token": "session_token0",
|
||||
}
|
||||
response = request.call(
|
||||
"CreateSession",
|
||||
options=options,
|
||||
)
|
||||
|
||||
assert response
|
||||
assert response.response == 0
|
||||
|
||||
session = xdp.Session.from_response(dbus_con, response)
|
||||
request = xdp.Request(dbus_con, remotedesktop_intf)
|
||||
options = {
|
||||
"types": dbus.UInt32(0x3),
|
||||
}
|
||||
response = request.call(
|
||||
"SelectDevices",
|
||||
session_handle=session.handle,
|
||||
options=options,
|
||||
)
|
||||
assert response
|
||||
assert response.response == 0
|
||||
|
||||
request = xdp.Request(dbus_con, remotedesktop_intf)
|
||||
options = {}
|
||||
response = request.call(
|
||||
"Start",
|
||||
session_handle=session.handle,
|
||||
parent_window="",
|
||||
options=options,
|
||||
)
|
||||
assert response
|
||||
assert response.response == 0
|
||||
|
||||
fd = remotedesktop_intf.ConnectToEIS(
|
||||
session.handle,
|
||||
dbus.Dictionary({}, signature="sv"),
|
||||
)
|
||||
eis_socket = socket.fromfd(fd.take(), socket.AF_UNIX, socket.SOCK_STREAM)
|
||||
assert eis_socket.recv(10) == b"HELLO"
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"template_params", ({"remotedesktop": {"fail-connect-to-eis": True}},)
|
||||
)
|
||||
def test_connect_to_eis_fail(self, portals, dbus_con):
|
||||
remotedesktop_intf = xdp.get_portal_iface(dbus_con, "RemoteDesktop")
|
||||
|
||||
request = xdp.Request(dbus_con, remotedesktop_intf)
|
||||
options = {
|
||||
"session_handle_token": "session_token0",
|
||||
}
|
||||
response = request.call(
|
||||
"CreateSession",
|
||||
options=options,
|
||||
)
|
||||
|
||||
assert response
|
||||
assert response.response == 0
|
||||
|
||||
session = xdp.Session.from_response(dbus_con, response)
|
||||
request = xdp.Request(dbus_con, remotedesktop_intf)
|
||||
options = {
|
||||
"types": dbus.UInt32(0x3),
|
||||
}
|
||||
response = request.call(
|
||||
"SelectDevices",
|
||||
session_handle=session.handle,
|
||||
options=options,
|
||||
)
|
||||
assert response
|
||||
assert response.response == 0
|
||||
|
||||
request = xdp.Request(dbus_con, remotedesktop_intf)
|
||||
options = {}
|
||||
response = request.call(
|
||||
"Start",
|
||||
session_handle=session.handle,
|
||||
parent_window="",
|
||||
options=options,
|
||||
)
|
||||
assert response
|
||||
assert response.response == 0
|
||||
|
||||
with pytest.raises(dbus.exceptions.DBusException) as excinfo:
|
||||
_ = remotedesktop_intf.ConnectToEIS(
|
||||
session.handle, dbus.Dictionary({}, signature="sv")
|
||||
)
|
||||
assert "Purposely failing ConnectToEIS" in excinfo.value.get_dbus_message()
|
||||
|
||||
def test_connect_to_eis_fail_notifies(self, portals, dbus_con):
|
||||
remotedesktop_intf = xdp.get_portal_iface(dbus_con, "RemoteDesktop")
|
||||
|
||||
request = xdp.Request(dbus_con, remotedesktop_intf)
|
||||
options = {
|
||||
"session_handle_token": "session_token0",
|
||||
}
|
||||
response = request.call(
|
||||
"CreateSession",
|
||||
options=options,
|
||||
)
|
||||
|
||||
assert response
|
||||
assert response.response == 0
|
||||
|
||||
session = xdp.Session.from_response(dbus_con, response)
|
||||
request = xdp.Request(dbus_con, remotedesktop_intf)
|
||||
options = {
|
||||
"types": dbus.UInt32(0x3),
|
||||
}
|
||||
response = request.call(
|
||||
"SelectDevices",
|
||||
session_handle=session.handle,
|
||||
options=options,
|
||||
)
|
||||
assert response
|
||||
assert response.response == 0
|
||||
|
||||
request = xdp.Request(dbus_con, remotedesktop_intf)
|
||||
options = {}
|
||||
response = request.call(
|
||||
"Start",
|
||||
session_handle=session.handle,
|
||||
parent_window="",
|
||||
options=options,
|
||||
)
|
||||
assert response
|
||||
assert response.response == 0
|
||||
|
||||
notifyfuncs: List[Dict[str, Any]] = [
|
||||
{"name": "NotifyPointerMotion", "args": (1, 2)},
|
||||
{"name": "NotifyPointerMotionAbsolute", "args": (0, 1, 2)},
|
||||
{"name": "NotifyPointerButton", "args": (1, 1)},
|
||||
{"name": "NotifyPointerAxis", "args": (1, 1)},
|
||||
{"name": "NotifyPointerAxisDiscrete", "args": (1, 1)},
|
||||
{"name": "NotifyKeyboardKeycode", "args": (1, 1)},
|
||||
{"name": "NotifyKeyboardKeysym", "args": (1, 1)},
|
||||
{"name": "NotifyTouchDown", "args": (0, 0, 1, 1)},
|
||||
{"name": "NotifyTouchMotion", "args": (0, 0, 1, 1)},
|
||||
{"name": "NotifyTouchUp", "args": (0,)},
|
||||
]
|
||||
for notifyfunc in notifyfuncs:
|
||||
with pytest.raises(dbus.exceptions.DBusException) as excinfo:
|
||||
func = getattr(remotedesktop_intf, notifyfunc["name"])
|
||||
assert func is not None
|
||||
func(
|
||||
session.handle,
|
||||
dbus.Dictionary({}, signature="sv"),
|
||||
*notifyfunc["args"],
|
||||
)
|
||||
# Not the best error message but...
|
||||
assert (
|
||||
"Session is not allowed to call Notify"
|
||||
in excinfo.value.get_dbus_message()
|
||||
)
|
||||
187
Telegram/ThirdParty/xdg-desktop-portal/tests/test_screenshot.py
vendored
Normal file
187
Telegram/ThirdParty/xdg-desktop-portal/tests/test_screenshot.py
vendored
Normal file
@@ -0,0 +1,187 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
#
|
||||
# This file is formatted with Python Black
|
||||
|
||||
import tests as xdp
|
||||
|
||||
import dbus
|
||||
import pytest
|
||||
from typing import Any
|
||||
|
||||
|
||||
SCREENSHOT_DATA = dbus.Dictionary(
|
||||
{
|
||||
"uri": "file:///screenshot.png",
|
||||
"color": (0.0, 1.0, 0.331),
|
||||
},
|
||||
signature="sv",
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def required_templates():
|
||||
return {
|
||||
"access": {},
|
||||
"screenshot": {
|
||||
"results": SCREENSHOT_DATA,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
class TestScreenshot:
|
||||
def test_version(self, portals, dbus_con):
|
||||
xdp.check_version(dbus_con, "Screenshot", 2)
|
||||
|
||||
@pytest.mark.parametrize("modal", [True, False])
|
||||
@pytest.mark.parametrize("interactive", [True, False])
|
||||
def test_screenshot_basic(self, portals, dbus_con, app_id, modal, interactive):
|
||||
screenshot_intf = xdp.get_portal_iface(dbus_con, "Screenshot")
|
||||
mock_intf = xdp.get_mock_iface(dbus_con)
|
||||
|
||||
request = xdp.Request(dbus_con, screenshot_intf)
|
||||
options = {
|
||||
"modal": modal,
|
||||
"interactive": interactive,
|
||||
}
|
||||
response = request.call(
|
||||
"Screenshot",
|
||||
parent_window="",
|
||||
options=options,
|
||||
)
|
||||
|
||||
assert response
|
||||
assert response.response == 0
|
||||
assert response.results["uri"] == SCREENSHOT_DATA["uri"]
|
||||
|
||||
# Check the impl portal was called with the right args
|
||||
method_calls = mock_intf.GetMethodCalls("Screenshot")
|
||||
assert len(method_calls) > 0
|
||||
_, args = method_calls[-1]
|
||||
assert args[1] == app_id
|
||||
assert args[2] == "" # parent window
|
||||
assert args[3]["modal"] == modal
|
||||
assert args[3]["interactive"] == interactive
|
||||
|
||||
# check that args were forwarded to access portal correctly
|
||||
if not interactive:
|
||||
method_calls = mock_intf.GetMethodCalls("AccessDialog")
|
||||
assert len(method_calls) > 0
|
||||
_, args = method_calls[-1]
|
||||
assert args[1] == app_id
|
||||
assert args[2] == "" # parent window
|
||||
assert args[6]["modal"] == modal
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"template_params", ({"screenshot": {"expect-close": True}},)
|
||||
)
|
||||
def test_screenshot_close(self, portals, dbus_con):
|
||||
screenshot_intf = xdp.get_portal_iface(dbus_con, "Screenshot")
|
||||
|
||||
request = xdp.Request(dbus_con, screenshot_intf)
|
||||
request.schedule_close(1000)
|
||||
options = {
|
||||
"interactive": True,
|
||||
}
|
||||
request.call(
|
||||
"Screenshot",
|
||||
parent_window="",
|
||||
options=options,
|
||||
)
|
||||
|
||||
# Only true if the impl.Request was closed too
|
||||
assert request.closed
|
||||
|
||||
@pytest.mark.parametrize("template_params", ({"screenshot": {"response": 1}},))
|
||||
def test_screenshot_cancel(self, portals, dbus_con, app_id):
|
||||
screenshot_intf = xdp.get_portal_iface(dbus_con, "Screenshot")
|
||||
mock_intf = xdp.get_mock_iface(dbus_con)
|
||||
|
||||
modal = True
|
||||
interactive = True
|
||||
|
||||
request = xdp.Request(dbus_con, screenshot_intf)
|
||||
options = {
|
||||
"modal": modal,
|
||||
"interactive": interactive,
|
||||
}
|
||||
response = request.call(
|
||||
"Screenshot",
|
||||
parent_window="",
|
||||
options=options,
|
||||
)
|
||||
|
||||
assert response
|
||||
assert response.response == 1
|
||||
|
||||
# Check the impl portal was called with the right args
|
||||
method_calls = mock_intf.GetMethodCalls("Screenshot")
|
||||
assert len(method_calls) > 0
|
||||
_, args = method_calls[-1]
|
||||
assert args[1] == app_id
|
||||
assert args[2] == "" # parent window
|
||||
assert args[3]["modal"] == modal
|
||||
assert args[3]["interactive"] == interactive
|
||||
|
||||
def test_pick_color_basic(self, portals, dbus_con, app_id):
|
||||
screenshot_intf = xdp.get_portal_iface(dbus_con, "Screenshot")
|
||||
mock_intf = xdp.get_mock_iface(dbus_con)
|
||||
|
||||
request = xdp.Request(dbus_con, screenshot_intf)
|
||||
options: Any = {}
|
||||
response = request.call(
|
||||
"PickColor",
|
||||
parent_window="",
|
||||
options=options,
|
||||
)
|
||||
|
||||
assert response
|
||||
assert response.response == 0
|
||||
assert response.results["color"] == SCREENSHOT_DATA["color"]
|
||||
|
||||
# Check the impl portal was called with the right args
|
||||
method_calls = mock_intf.GetMethodCalls("PickColor")
|
||||
assert len(method_calls) > 0
|
||||
_, args = method_calls[-1]
|
||||
assert args[1] == app_id
|
||||
assert args[2] == "" # parent window
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"template_params", ({"screenshot": {"expect-close": True}},)
|
||||
)
|
||||
def test_pick_color_close(self, portals, dbus_con, app_id):
|
||||
screenshot_intf = xdp.get_portal_iface(dbus_con, "Screenshot")
|
||||
|
||||
request = xdp.Request(dbus_con, screenshot_intf)
|
||||
request.schedule_close(1000)
|
||||
options: Any = {}
|
||||
request.call(
|
||||
"PickColor",
|
||||
parent_window="",
|
||||
options=options,
|
||||
)
|
||||
|
||||
# Only true if the impl.Request was closed too
|
||||
assert request.closed
|
||||
|
||||
@pytest.mark.parametrize("template_params", ({"screenshot": {"response": 1}},))
|
||||
def test_pick_color_cancel(self, portals, dbus_con, app_id):
|
||||
screenshot_intf = xdp.get_portal_iface(dbus_con, "Screenshot")
|
||||
mock_intf = xdp.get_mock_iface(dbus_con)
|
||||
|
||||
request = xdp.Request(dbus_con, screenshot_intf)
|
||||
options: Any = {}
|
||||
response = request.call(
|
||||
"PickColor",
|
||||
parent_window="",
|
||||
options=options,
|
||||
)
|
||||
|
||||
assert response
|
||||
assert response.response == 1
|
||||
|
||||
# Check the impl portal was called with the right args
|
||||
method_calls = mock_intf.GetMethodCalls("PickColor")
|
||||
assert len(method_calls) > 0
|
||||
_, args = method_calls[-1]
|
||||
assert args[1] == app_id
|
||||
assert args[2] == "" # parent window
|
||||
338
Telegram/ThirdParty/xdg-desktop-portal/tests/test_settings.py
vendored
Normal file
338
Telegram/ThirdParty/xdg-desktop-portal/tests/test_settings.py
vendored
Normal file
@@ -0,0 +1,338 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
#
|
||||
# This file is formatted with Python Black
|
||||
|
||||
import tests as xdp
|
||||
|
||||
import dbus
|
||||
import pytest
|
||||
|
||||
|
||||
SETTINGS_DATA_TEST1 = {
|
||||
"org.freedesktop.appearance": dbus.Dictionary(
|
||||
{
|
||||
"color-scheme": dbus.UInt32(1),
|
||||
"accent-color": dbus.Struct((0.0, 0.1, 0.33), signature="ddd"),
|
||||
},
|
||||
signature="sv",
|
||||
),
|
||||
}
|
||||
|
||||
SETTINGS_DATA_TEST2 = {
|
||||
"org.freedesktop.appearance": dbus.Dictionary(
|
||||
{
|
||||
"color-scheme": dbus.UInt32(2),
|
||||
"contrast": dbus.UInt32(0),
|
||||
},
|
||||
signature="sv",
|
||||
),
|
||||
"org.example.custom": dbus.Dictionary(
|
||||
{
|
||||
"foo": "bar",
|
||||
},
|
||||
signature="sv",
|
||||
),
|
||||
}
|
||||
|
||||
SETTINGS_DATA_BAD = {
|
||||
"org.freedesktop.appearance": dbus.Dictionary(
|
||||
{
|
||||
"color-scheme": dbus.UInt32(99),
|
||||
"accent-color": dbus.Struct((11.11, 22.22, 33.33), signature="ddd"),
|
||||
},
|
||||
signature="sv",
|
||||
),
|
||||
"org.example.custom": dbus.Dictionary(
|
||||
{
|
||||
"foo": "baz",
|
||||
},
|
||||
signature="sv",
|
||||
),
|
||||
"org.example.custom.bad": dbus.Dictionary(
|
||||
{
|
||||
"bad": "bad",
|
||||
},
|
||||
signature="sv",
|
||||
),
|
||||
}
|
||||
|
||||
# This is the expected data, merged SETTINGS_DATA_TEST1 and SETTINGS_DATA_TEST2
|
||||
SETTINGS_DATA = {
|
||||
"org.freedesktop.appearance": dbus.Dictionary(
|
||||
{
|
||||
"color-scheme": dbus.UInt32(1),
|
||||
"accent-color": dbus.Struct((0.0, 0.1, 0.33), signature="ddd"),
|
||||
"contrast": dbus.UInt32(0),
|
||||
},
|
||||
signature="sv",
|
||||
),
|
||||
"org.example.custom": dbus.Dictionary(
|
||||
{
|
||||
"foo": "bar",
|
||||
},
|
||||
signature="sv",
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def required_templates():
|
||||
return {
|
||||
"settings:org.freedesktop.impl.portal.Test1": {
|
||||
"settings": SETTINGS_DATA_TEST1,
|
||||
},
|
||||
"settings:org.freedesktop.impl.portal.Test2": {
|
||||
"settings": SETTINGS_DATA_TEST2,
|
||||
},
|
||||
"settings:org.freedesktop.impl.portal.TestBad": {
|
||||
"settings": SETTINGS_DATA_BAD,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
PORTAL_CONFIG_FILES = {
|
||||
"test1.portal": b"""
|
||||
[portal]
|
||||
DBusName=org.freedesktop.impl.portal.Test1
|
||||
Interfaces=org.freedesktop.impl.portal.Settings;
|
||||
""",
|
||||
"test2.portal": b"""
|
||||
[portal]
|
||||
DBusName=org.freedesktop.impl.portal.Test2
|
||||
Interfaces=org.freedesktop.impl.portal.Settings;
|
||||
""",
|
||||
"test_bad.portal": b"""
|
||||
[portal]
|
||||
DBusName=org.freedesktop.impl.portal.TestBad
|
||||
Interfaces=org.freedesktop.impl.portal.Settings;
|
||||
""",
|
||||
"test_noimpl.portal": b"""
|
||||
[portal]
|
||||
DBusName=org.freedesktop.impl.portal.TestBad
|
||||
Interfaces=org.freedesktop.impl.portal.NonExistant;
|
||||
""",
|
||||
}
|
||||
|
||||
|
||||
def portal_config_good():
|
||||
# test1 merged with test2 should result in the correct output
|
||||
files = PORTAL_CONFIG_FILES.copy()
|
||||
files["test-portals.conf"] = b"""
|
||||
[preferred]
|
||||
default=test1;test2;
|
||||
"""
|
||||
yield files
|
||||
|
||||
# a portal without the settings impl does not affect the result
|
||||
files = PORTAL_CONFIG_FILES.copy()
|
||||
files["test-portals.conf"] = b"""
|
||||
[preferred]
|
||||
default=test1;test_noimpl;test2;
|
||||
"""
|
||||
yield files
|
||||
|
||||
# the default should be ignored when the interface is configured
|
||||
files = PORTAL_CONFIG_FILES.copy()
|
||||
files["test-portals.conf"] = b"""
|
||||
[preferred]
|
||||
default=test_bad;
|
||||
org.freedesktop.impl.portal.Settings=test1;test2
|
||||
"""
|
||||
yield files
|
||||
|
||||
# use * which should expand to test1;test2;test_noimpl
|
||||
files = PORTAL_CONFIG_FILES.copy()
|
||||
del files["test_bad.portal"]
|
||||
files["test-portals.conf"] = b"""
|
||||
[preferred]
|
||||
default=test_noimpl;
|
||||
org.freedesktop.impl.portal.Settings=*;
|
||||
"""
|
||||
yield files
|
||||
|
||||
|
||||
def portal_config_bad():
|
||||
# test1 alone should result in bad output
|
||||
files = PORTAL_CONFIG_FILES.copy()
|
||||
files["test-portals.conf"] = b"""
|
||||
[preferred]
|
||||
default=test1;
|
||||
"""
|
||||
yield files
|
||||
|
||||
# test2 merged with test1 is the wrong order
|
||||
files = PORTAL_CONFIG_FILES.copy()
|
||||
files["test-portals.conf"] = b"""
|
||||
[preferred]
|
||||
default=test2;test1;
|
||||
"""
|
||||
yield files
|
||||
|
||||
# test_noimpl does not affect anything
|
||||
files = PORTAL_CONFIG_FILES.copy()
|
||||
files["test-portals.conf"] = b"""
|
||||
[preferred]
|
||||
default=test_noimpl;test2;test1;
|
||||
"""
|
||||
yield files
|
||||
|
||||
# default should get ignored, test2 alone should result in bad output
|
||||
files = PORTAL_CONFIG_FILES.copy()
|
||||
files["test-portals.conf"] = b"""
|
||||
[preferred]
|
||||
default=test1;test2
|
||||
org.freedesktop.impl.portal.Settings=test2;test_noimpl
|
||||
"""
|
||||
yield files
|
||||
|
||||
# test_bad anywhere in the active config should result in bad output
|
||||
files = PORTAL_CONFIG_FILES.copy()
|
||||
files["test-portals.conf"] = b"""
|
||||
[preferred]
|
||||
default=test1;test2
|
||||
org.freedesktop.impl.portal.Settings=test_bad;test1;test2
|
||||
"""
|
||||
yield files
|
||||
|
||||
# use * which expands to test1;test2;test_bad;test_no_impl
|
||||
# contains test_bad which should result in bad output
|
||||
files = PORTAL_CONFIG_FILES.copy()
|
||||
files["test-portals.conf"] = b"""
|
||||
[preferred]
|
||||
default=test_noimpl;
|
||||
org.freedesktop.impl.portal.Settings=*;
|
||||
"""
|
||||
yield files
|
||||
|
||||
|
||||
def portal_config_twice():
|
||||
# check that test1 gets picked up only once
|
||||
files = PORTAL_CONFIG_FILES.copy()
|
||||
del files["test_bad.portal"]
|
||||
files["test-portals.conf"] = b"""
|
||||
[preferred]
|
||||
default=test_noimpl;
|
||||
org.freedesktop.impl.portal.Settings=test1;*;
|
||||
"""
|
||||
yield files
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def xdg_desktop_portal_dir_default_files():
|
||||
return next(portal_config_good())
|
||||
|
||||
|
||||
class TestSettings:
|
||||
def test_version(self, portals, dbus_con):
|
||||
xdp.check_version(dbus_con, "Settings", 2)
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"xdg_desktop_portal_dir_default_files",
|
||||
portal_config_good(),
|
||||
)
|
||||
def test_read_all(self, portals, dbus_con):
|
||||
settings_intf = xdp.get_portal_iface(dbus_con, "Settings")
|
||||
|
||||
value = settings_intf.ReadAll([])
|
||||
assert value == SETTINGS_DATA
|
||||
|
||||
value = settings_intf.ReadAll([""])
|
||||
assert value == SETTINGS_DATA
|
||||
|
||||
value = settings_intf.ReadAll(["does-not-exist"])
|
||||
assert value == {}
|
||||
|
||||
value = settings_intf.ReadAll(["org."])
|
||||
assert value == {}
|
||||
|
||||
value = settings_intf.ReadAll(["org.*"])
|
||||
assert value == SETTINGS_DATA
|
||||
|
||||
value = settings_intf.ReadAll(
|
||||
["org.freedesktop.appearance", "org.example.custom"]
|
||||
)
|
||||
assert value == SETTINGS_DATA
|
||||
|
||||
value = settings_intf.ReadAll(["org.freedesktop.appearance"])
|
||||
assert len(value) == 1
|
||||
assert "org.freedesktop.appearance" in value
|
||||
assert (
|
||||
value["org.freedesktop.appearance"]
|
||||
== SETTINGS_DATA["org.freedesktop.appearance"]
|
||||
)
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"xdg_desktop_portal_dir_default_files",
|
||||
portal_config_bad(),
|
||||
)
|
||||
def test_read_all_bad_config(self, portals, dbus_con):
|
||||
settings_intf = xdp.get_portal_iface(dbus_con, "Settings")
|
||||
|
||||
value = settings_intf.ReadAll([])
|
||||
assert value != SETTINGS_DATA
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"xdg_desktop_portal_dir_default_files",
|
||||
portal_config_twice(),
|
||||
)
|
||||
def test_config_twice(self, portals, dbus_con):
|
||||
settings_intf = xdp.get_portal_iface(dbus_con, "Settings")
|
||||
mock_intf = xdp.get_mock_iface(dbus_con, "org.freedesktop.impl.portal.Test1")
|
||||
|
||||
value = settings_intf.ReadAll([])
|
||||
assert value == SETTINGS_DATA
|
||||
|
||||
# The config is `test1;*`, make sure we only get a single call to Test1
|
||||
method_calls = mock_intf.GetMethodCalls("ReadAll")
|
||||
assert len(method_calls) == 1
|
||||
|
||||
def test_read(self, portals, dbus_con):
|
||||
settings_intf = xdp.get_portal_iface(dbus_con, "Settings")
|
||||
|
||||
color_scheme = SETTINGS_DATA["org.freedesktop.appearance"]["color-scheme"]
|
||||
|
||||
value = settings_intf.ReadOne("org.freedesktop.appearance", "color-scheme")
|
||||
assert isinstance(value, dbus.UInt32)
|
||||
assert value.variant_level == 1
|
||||
assert value == color_scheme
|
||||
|
||||
with pytest.raises(dbus.exceptions.DBusException) as excinfo:
|
||||
settings_intf.ReadOne("org.does.not.exist", "color-scheme")
|
||||
assert excinfo.value.get_dbus_name() == "org.freedesktop.portal.Error.NotFound"
|
||||
|
||||
with pytest.raises(dbus.exceptions.DBusException) as excinfo:
|
||||
settings_intf.ReadOne("org.freedesktop.appearance", "xcolor-scheme")
|
||||
assert excinfo.value.get_dbus_name() == "org.freedesktop.portal.Error.NotFound"
|
||||
|
||||
# deprecated but should still check that it works
|
||||
# the crucial detail here is that the variant_level is 2
|
||||
value = settings_intf.Read("org.freedesktop.appearance", "color-scheme")
|
||||
assert isinstance(value, dbus.UInt32)
|
||||
assert value.variant_level == 2
|
||||
assert value == color_scheme
|
||||
|
||||
def test_changed(self, portals, dbus_con):
|
||||
settings_intf = xdp.get_portal_iface(dbus_con, "Settings")
|
||||
mock_intf = xdp.get_mock_iface(dbus_con, "org.freedesktop.impl.portal.Test1")
|
||||
changed_count = 0
|
||||
|
||||
ns = "org.freedesktop.appearance"
|
||||
key = "color-scheme"
|
||||
current_value = SETTINGS_DATA[ns][key]
|
||||
new_value = 2
|
||||
assert current_value != new_value
|
||||
|
||||
value = settings_intf.ReadOne(ns, key)
|
||||
assert value == current_value
|
||||
|
||||
def cb_settings_changed(changed_ns, changed_key, changed_value):
|
||||
nonlocal changed_count
|
||||
changed_count += 1
|
||||
assert changed_ns == ns
|
||||
assert changed_key == key
|
||||
assert changed_value == new_value
|
||||
|
||||
settings_intf.connect_to_signal("SettingChanged", cb_settings_changed)
|
||||
mock_intf.SetSetting(ns, key, new_value)
|
||||
|
||||
xdp.wait_for(lambda: changed_count == 1)
|
||||
31
Telegram/ThirdParty/xdg-desktop-portal/tests/test_trash.py
vendored
Normal file
31
Telegram/ThirdParty/xdg-desktop-portal/tests/test_trash.py
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
#
|
||||
# This file is formatted with Python Black
|
||||
|
||||
import tests as xdp
|
||||
|
||||
import os
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
class TestTrash:
|
||||
def test_version(self, portals, dbus_con):
|
||||
xdp.check_version(dbus_con, "Trash", 1)
|
||||
|
||||
def test_trash_file_fails(self, portals, dbus_con):
|
||||
trash_intf = xdp.get_portal_iface(dbus_con, "Trash")
|
||||
with open("/proc/cmdline") as fd:
|
||||
result = trash_intf.TrashFile(fd.fileno())
|
||||
|
||||
assert result == 0
|
||||
|
||||
def test_trash_file(self, portals, dbus_con):
|
||||
trash_intf = xdp.get_portal_iface(dbus_con, "Trash")
|
||||
|
||||
fd, name = tempfile.mkstemp(prefix="trash_portal_mock_", dir=Path.home())
|
||||
result = trash_intf.TrashFile(fd)
|
||||
if result != 1:
|
||||
os.unlink(name)
|
||||
assert result == 1
|
||||
assert not Path(name).exists()
|
||||
384
Telegram/ThirdParty/xdg-desktop-portal/tests/test_usb.py
vendored
Normal file
384
Telegram/ThirdParty/xdg-desktop-portal/tests/test_usb.py
vendored
Normal file
@@ -0,0 +1,384 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
#
|
||||
# This file is formatted with Python Black
|
||||
|
||||
import tests as xdp
|
||||
|
||||
import pytest
|
||||
import os
|
||||
import gi
|
||||
import subprocess
|
||||
import re
|
||||
|
||||
gi.require_version("UMockdev", "1.0")
|
||||
from gi.repository import GLib, UMockdev # noqa E402
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def required_templates():
|
||||
return {"usb": {}}
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def umockdev():
|
||||
return UMockdev.Testbed.new()
|
||||
|
||||
|
||||
def umockdev_has_working_remove():
|
||||
# umockdev only generates remove events since version 0.18.4
|
||||
# https://github.com/martinpitt/umockdev/releases/tag/0.18.4
|
||||
required = (0, 18, 4)
|
||||
|
||||
result = subprocess.run(["umockdev-run", "--version"], stdout=subprocess.PIPE)
|
||||
if result.returncode != 0:
|
||||
return False
|
||||
match = re.match(r"^(\d+)\.(\d+)\.(\d+)", result.stdout.decode("UTF-8").strip())
|
||||
if not match:
|
||||
return False
|
||||
version = tuple(map(int, match.groups()))
|
||||
return version >= required
|
||||
|
||||
|
||||
class TestUsb:
|
||||
_num_devices = 0
|
||||
|
||||
def generate_device(
|
||||
self, testbed, vendor, vendor_name, product, product_name, serial
|
||||
):
|
||||
n = self._num_devices
|
||||
self._num_devices += 1
|
||||
|
||||
testbed.add_from_string(f"""P: /devices/usb{n}
|
||||
N: bus/usb/001/{n:03d}
|
||||
E: BUSNUM=001
|
||||
E: DEVNUM={n:03d}
|
||||
E: DEVNAME=/dev/bus/usb/001/{n:03d}
|
||||
E: DEVTYPE=usb_device
|
||||
E: DRIVER=usb
|
||||
E: ID_BUS=usb
|
||||
E: ID_MODEL={product_name}
|
||||
E: ID_MODEL_ID={product}
|
||||
E: ID_REVISION=0002
|
||||
E: ID_SERIAL={vendor_name}_{product_name}_{serial}
|
||||
E: ID_SERIAL_SHORT={serial}
|
||||
E: ID_VENDOR={vendor_name}
|
||||
E: ID_VENDOR_ID={vendor}
|
||||
E: SUBSYSTEM=usb
|
||||
A: idProduct={product}
|
||||
A: idVendor={vendor}
|
||||
""")
|
||||
|
||||
return f"/sys/devices/usb{n}"
|
||||
|
||||
def test_version(self, portals, dbus_con):
|
||||
xdp.check_version(dbus_con, "Usb", 1)
|
||||
|
||||
def test_create_close_session(self, portals, dbus_con, app_id):
|
||||
usb_intf = xdp.get_portal_iface(dbus_con, "Usb")
|
||||
|
||||
session = xdp.Session(
|
||||
dbus_con,
|
||||
usb_intf.CreateSession({"session_handle_token": "session_token0"}),
|
||||
)
|
||||
session.close()
|
||||
|
||||
def test_empty_initial_devices(self, portals, dbus_con, app_id):
|
||||
usb_intf = xdp.get_portal_iface(dbus_con, "Usb")
|
||||
|
||||
xdp.Session(
|
||||
dbus_con,
|
||||
usb_intf.CreateSession({"session_handle_token": "session_token0"}),
|
||||
)
|
||||
|
||||
device_events_signal_received = False
|
||||
|
||||
def cb_device_events(session_handle, events):
|
||||
nonlocal device_events_signal_received
|
||||
device_events_signal_received = True
|
||||
|
||||
usb_intf.connect_to_signal("DeviceEvents", cb_device_events)
|
||||
xdp.wait(300)
|
||||
assert not device_events_signal_received
|
||||
|
||||
@pytest.mark.parametrize("usb_queries", ["vnd:04a9", None])
|
||||
def test_initial_devices(self, portals, dbus_con, app_id, usb_queries, umockdev):
|
||||
usb_intf = xdp.get_portal_iface(dbus_con, "Usb")
|
||||
|
||||
self.generate_device(
|
||||
umockdev,
|
||||
"04a9",
|
||||
"Canon_Inc.",
|
||||
"31c0",
|
||||
"Canon_Digital_Camera",
|
||||
"C767F1C714174C309255F70E4A7B2EE2",
|
||||
)
|
||||
|
||||
# TODO: to make this more robust, we should find a way to wait for
|
||||
# the portal to pick up the device
|
||||
xdp.wait(300)
|
||||
|
||||
session = xdp.Session(
|
||||
dbus_con,
|
||||
usb_intf.CreateSession({"session_handle_token": "session_token0"}),
|
||||
)
|
||||
|
||||
device_events_signal_received = False
|
||||
devices_received = 0
|
||||
|
||||
def cb_device_events(session_handle, events):
|
||||
nonlocal device_events_signal_received
|
||||
nonlocal devices_received
|
||||
assert session.handle == session_handle
|
||||
|
||||
for action, id, device in events:
|
||||
assert action == "add"
|
||||
devices_received += 1
|
||||
|
||||
device_events_signal_received = True
|
||||
|
||||
usb_intf.connect_to_signal("DeviceEvents", cb_device_events)
|
||||
|
||||
if usb_queries is None:
|
||||
xdp.wait(300)
|
||||
assert not device_events_signal_received
|
||||
assert devices_received == 0
|
||||
else:
|
||||
xdp.wait_for(lambda: device_events_signal_received)
|
||||
assert devices_received == 1
|
||||
|
||||
@pytest.mark.parametrize("usb_queries", ["vnd:04a9", None])
|
||||
def test_device_add(self, portals, dbus_con, app_id, usb_queries, umockdev):
|
||||
usb_intf = xdp.get_portal_iface(dbus_con, "Usb")
|
||||
|
||||
session = xdp.Session(
|
||||
dbus_con,
|
||||
usb_intf.CreateSession({"session_handle_token": "session_token0"}),
|
||||
)
|
||||
|
||||
device_events_signal_received = False
|
||||
devices_received = 0
|
||||
device = None
|
||||
|
||||
def cb_device_events(session_handle, events):
|
||||
nonlocal device_events_signal_received
|
||||
nonlocal devices_received
|
||||
nonlocal device
|
||||
assert session.handle == session_handle
|
||||
|
||||
for action, _, dev in events:
|
||||
assert action == "add"
|
||||
device = dev
|
||||
devices_received += 1
|
||||
|
||||
device_events_signal_received = True
|
||||
|
||||
usb_intf.connect_to_signal("DeviceEvents", cb_device_events)
|
||||
xdp.wait(300)
|
||||
assert not device_events_signal_received
|
||||
|
||||
self.generate_device(
|
||||
umockdev,
|
||||
"04a9",
|
||||
"Canon_Inc.",
|
||||
"31c0",
|
||||
"Canon_Digital_Camera",
|
||||
"C767F1C714174C309255F70E4A7B2EE2",
|
||||
)
|
||||
|
||||
if usb_queries is None:
|
||||
xdp.wait(300)
|
||||
assert not device_events_signal_received
|
||||
assert devices_received == 0
|
||||
else:
|
||||
xdp.wait_for(lambda: device_events_signal_received)
|
||||
assert devices_received == 1
|
||||
|
||||
assert device
|
||||
assert device["readable"]
|
||||
assert device["writable"]
|
||||
assert device["device-file"] == "/dev/bus/usb/001/000"
|
||||
assert device["properties"]["ID_VENDOR_ID"] == "04a9"
|
||||
assert device["properties"]["ID_MODEL_ID"] == "31c0"
|
||||
assert (
|
||||
device["properties"]["ID_SERIAL"]
|
||||
== "Canon_Inc._Canon_Digital_Camera_C767F1C714174C309255F70E4A7B2EE2"
|
||||
)
|
||||
|
||||
@pytest.mark.parametrize("usb_queries", ["vnd:04a9", None])
|
||||
@pytest.mark.skipif(
|
||||
not umockdev_has_working_remove(), reason="UMockdev version 0.18.4 required"
|
||||
)
|
||||
def test_device_remove(self, portals, dbus_con, app_id, usb_queries, umockdev):
|
||||
usb_intf = xdp.get_portal_iface(dbus_con, "Usb")
|
||||
|
||||
dev_path = self.generate_device(
|
||||
umockdev,
|
||||
"04a9",
|
||||
"Canon_Inc.",
|
||||
"31c0",
|
||||
"Canon_Digital_Camera",
|
||||
"C767F1C714174C309255F70E4A7B2EE2",
|
||||
)
|
||||
|
||||
session = xdp.Session(
|
||||
dbus_con,
|
||||
usb_intf.CreateSession({"session_handle_token": "session_token0"}),
|
||||
)
|
||||
|
||||
device_events_signal_count = 0
|
||||
devices_received = 0
|
||||
devices_removed = 0
|
||||
|
||||
def cb_device_events(session_handle, events):
|
||||
nonlocal device_events_signal_count
|
||||
nonlocal devices_received
|
||||
nonlocal devices_removed
|
||||
|
||||
assert session.handle == session_handle
|
||||
|
||||
for action, id, device in events:
|
||||
if action == "add":
|
||||
devices_received += 1
|
||||
elif action == "remove":
|
||||
devices_removed += 1
|
||||
else:
|
||||
assert False
|
||||
|
||||
device_events_signal_count += 1
|
||||
|
||||
usb_intf.connect_to_signal("DeviceEvents", cb_device_events)
|
||||
|
||||
if usb_queries is None:
|
||||
xdp.wait(300)
|
||||
assert device_events_signal_count == 0
|
||||
assert devices_received == 0
|
||||
assert devices_removed == 0
|
||||
else:
|
||||
xdp.wait_for(lambda: device_events_signal_count == 1)
|
||||
assert devices_received == 1
|
||||
assert devices_removed == 0
|
||||
|
||||
umockdev.remove_device(dev_path)
|
||||
|
||||
if usb_queries is None:
|
||||
xdp.wait(300)
|
||||
assert device_events_signal_count == 0
|
||||
assert devices_received == 0
|
||||
assert devices_removed == 0
|
||||
else:
|
||||
xdp.wait_for(lambda: device_events_signal_count == 2)
|
||||
assert devices_received == 1
|
||||
assert devices_removed == 1
|
||||
|
||||
@pytest.mark.parametrize("usb_queries", ["vnd:04a9;vnd:04aa"])
|
||||
@pytest.mark.parametrize(
|
||||
"template_params", [{"usb": {"filters": {"vendor": "04a9"}}}]
|
||||
)
|
||||
def test_acquire(self, portals, dbus_con, app_id, umockdev):
|
||||
usb_intf = xdp.get_portal_iface(dbus_con, "Usb")
|
||||
|
||||
self.generate_device(
|
||||
umockdev,
|
||||
"04a9",
|
||||
"Canon_Inc.",
|
||||
"31c0",
|
||||
"Canon_Digital_Camera",
|
||||
"C767F1C714174C309255F70E4A7B2EE2",
|
||||
)
|
||||
|
||||
self.generate_device(
|
||||
umockdev,
|
||||
"04aa",
|
||||
"Someone Else.",
|
||||
"31c0",
|
||||
"SomeProduct",
|
||||
"00001",
|
||||
)
|
||||
|
||||
possible_vendors = ["04a9", "04aa"]
|
||||
|
||||
devices = usb_intf.EnumerateDevices({})
|
||||
assert len(devices) == 2
|
||||
(id1, dev_info1) = devices[0]
|
||||
assert id1
|
||||
assert dev_info1
|
||||
vendor_id = dev_info1["properties"]["ID_VENDOR_ID"]
|
||||
assert vendor_id in possible_vendors
|
||||
possible_vendors.remove(vendor_id)
|
||||
(id2, dev_info2) = devices[1]
|
||||
assert id2
|
||||
assert dev_info2
|
||||
vendor_id = dev_info2["properties"]["ID_VENDOR_ID"]
|
||||
assert vendor_id in possible_vendors
|
||||
possible_vendors.remove(vendor_id)
|
||||
|
||||
request = xdp.Request(dbus_con, usb_intf)
|
||||
response = request.call(
|
||||
"AcquireDevices",
|
||||
parent_window="",
|
||||
devices=[
|
||||
(id1, {"writable": True}),
|
||||
(id2, {"writable": True}),
|
||||
],
|
||||
options={},
|
||||
)
|
||||
assert response
|
||||
assert response.response == 0
|
||||
|
||||
(results, finished) = usb_intf.FinishAcquireDevices(request.handle, {})
|
||||
assert finished
|
||||
assert len(results) == 1
|
||||
(res_id, device) = results[0]
|
||||
assert res_id == id1 or res_id == id2
|
||||
assert device["success"]
|
||||
fd = device["fd"].take()
|
||||
assert fd > 0
|
||||
with os.fdopen(fd, "r") as f:
|
||||
assert f
|
||||
assert "error" not in device
|
||||
|
||||
usb_intf.ReleaseDevices([res_id], {})
|
||||
|
||||
@pytest.mark.parametrize("usb_queries", ["vnd:0001"])
|
||||
@pytest.mark.parametrize(
|
||||
"expected,template_params",
|
||||
[
|
||||
(1, {"usb": {"filters": {"model": "0000"}}}),
|
||||
(1, {"usb": {"filters": {"model": "0001"}}}),
|
||||
(0, {"usb": {"filters": {"model": "0002"}}}),
|
||||
(2, {"usb": {"filters": {"vendor": "0001"}}}),
|
||||
(0, {"usb": {"filters": {"vendor": "0002"}}}),
|
||||
(1, {"usb": {"filters": {"vendor": "0001", "model": "0000"}}}),
|
||||
(0, {"usb": {"filters": {"vendor": "0002", "model": "0000"}}}),
|
||||
],
|
||||
)
|
||||
def test_queries(self, portals, dbus_con, expected, app_id, usb_queries, umockdev):
|
||||
usb_intf = xdp.get_portal_iface(dbus_con, "Usb")
|
||||
|
||||
for i in range(2):
|
||||
self.generate_device(
|
||||
umockdev,
|
||||
"0001",
|
||||
"example_org",
|
||||
f"000{i}",
|
||||
f"model{i}",
|
||||
"0001",
|
||||
)
|
||||
|
||||
devices = usb_intf.EnumerateDevices({})
|
||||
assert len(devices) == 2
|
||||
acquire_devices = [(id, {"writable": True}) for (id, _) in devices]
|
||||
|
||||
request = xdp.Request(dbus_con, usb_intf)
|
||||
response = request.call(
|
||||
"AcquireDevices",
|
||||
parent_window="",
|
||||
devices=acquire_devices,
|
||||
options={},
|
||||
)
|
||||
assert response
|
||||
assert response.response == 0
|
||||
|
||||
(results, finished) = usb_intf.FinishAcquireDevices(request.handle, {})
|
||||
assert finished
|
||||
assert len(results) == expected
|
||||
153
Telegram/ThirdParty/xdg-desktop-portal/tests/test_wallpaper.py
vendored
Normal file
153
Telegram/ThirdParty/xdg-desktop-portal/tests/test_wallpaper.py
vendored
Normal file
@@ -0,0 +1,153 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
#
|
||||
# This file is formatted with Python Black
|
||||
|
||||
import tests as xdp
|
||||
|
||||
import pytest
|
||||
import os
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def required_templates():
|
||||
return {"wallpaper": {}}
|
||||
|
||||
|
||||
class TestWallpaper:
|
||||
def set_permission(self, dbus_con, app_id, permission):
|
||||
perm_store_intf = xdp.get_permission_store_iface(dbus_con)
|
||||
perm_store_intf.SetPermission(
|
||||
"wallpaper",
|
||||
True,
|
||||
"wallpaper",
|
||||
app_id,
|
||||
[permission],
|
||||
)
|
||||
|
||||
def test_version(self, portals, dbus_con):
|
||||
xdp.check_version(dbus_con, "Wallpaper", 1)
|
||||
|
||||
def test_wallpaper_uri(self, portals, dbus_con, app_id):
|
||||
wallpaper_intf = xdp.get_portal_iface(dbus_con, "Wallpaper")
|
||||
mock_intf = xdp.get_mock_iface(dbus_con)
|
||||
|
||||
uri = "file:///test"
|
||||
show_preview = True
|
||||
set_on = "both"
|
||||
|
||||
request = xdp.Request(dbus_con, wallpaper_intf)
|
||||
options = {
|
||||
"show-preview": show_preview,
|
||||
"set-on": set_on,
|
||||
}
|
||||
response = request.call(
|
||||
"SetWallpaperURI",
|
||||
parent_window="",
|
||||
uri=uri,
|
||||
options=options,
|
||||
)
|
||||
|
||||
assert response
|
||||
assert response.response == 0
|
||||
|
||||
# Check the impl portal was called with the right args
|
||||
method_calls = mock_intf.GetMethodCalls("SetWallpaperURI")
|
||||
assert len(method_calls) > 0
|
||||
_, args = method_calls[-1]
|
||||
assert args[1] == app_id
|
||||
assert args[2] == "" # parent window
|
||||
assert args[3] == uri
|
||||
assert args[4]["show-preview"] == show_preview
|
||||
assert args[4]["set-on"] == set_on
|
||||
|
||||
def test_wallpaper_file(self, portals, dbus_con, app_id):
|
||||
wallpaper_intf = xdp.get_portal_iface(dbus_con, "Wallpaper")
|
||||
mock_intf = xdp.get_mock_iface(dbus_con)
|
||||
|
||||
fd, _ = tempfile.mkstemp(prefix="wallpaper_mock", dir=Path.home())
|
||||
os.write(fd, b"wallpaper_mock_file")
|
||||
|
||||
show_preview = True
|
||||
|
||||
request = xdp.Request(dbus_con, wallpaper_intf)
|
||||
options = {
|
||||
"show-preview": show_preview,
|
||||
}
|
||||
response = request.call(
|
||||
"SetWallpaperFile",
|
||||
parent_window="",
|
||||
fd=fd,
|
||||
options=options,
|
||||
)
|
||||
|
||||
assert response
|
||||
assert response.response == 0
|
||||
|
||||
# Check the impl portal was called with the right args
|
||||
method_calls = mock_intf.GetMethodCalls("SetWallpaperURI")
|
||||
assert len(method_calls) > 0
|
||||
_, args = method_calls[-1]
|
||||
assert args[1] == app_id
|
||||
assert args[2] == "" # parent window
|
||||
assert args[4]["show-preview"] == show_preview
|
||||
|
||||
path = args[3]
|
||||
assert path.startswith("file:///")
|
||||
|
||||
with open(path[7:]) as file:
|
||||
wallpaper_file_contents = file.read()
|
||||
assert wallpaper_file_contents == "wallpaper_mock_file"
|
||||
|
||||
@pytest.mark.parametrize("template_params", ({"wallpaper": {"response": 1}},))
|
||||
def test_wallpaper_cancel(self, portals, dbus_con, app_id):
|
||||
wallpaper_intf = xdp.get_portal_iface(dbus_con, "Wallpaper")
|
||||
|
||||
uri = "file:///test"
|
||||
show_preview = True
|
||||
set_on = "both"
|
||||
|
||||
request = xdp.Request(dbus_con, wallpaper_intf)
|
||||
options = {
|
||||
"show-preview": show_preview,
|
||||
"set-on": set_on,
|
||||
}
|
||||
response = request.call(
|
||||
"SetWallpaperURI",
|
||||
parent_window="",
|
||||
uri=uri,
|
||||
options=options,
|
||||
)
|
||||
|
||||
assert response
|
||||
assert response.response == 1
|
||||
|
||||
def test_wallpaper_permission(self, portals, dbus_con, app_id):
|
||||
wallpaper_intf = xdp.get_portal_iface(dbus_con, "Wallpaper")
|
||||
mock_intf = xdp.get_mock_iface(dbus_con)
|
||||
|
||||
self.set_permission(dbus_con, app_id, "no")
|
||||
|
||||
uri = "file:///test"
|
||||
show_preview = True
|
||||
set_on = "both"
|
||||
|
||||
request = xdp.Request(dbus_con, wallpaper_intf)
|
||||
options = {
|
||||
"show-preview": show_preview,
|
||||
"set-on": set_on,
|
||||
}
|
||||
response = request.call(
|
||||
"SetWallpaperURI",
|
||||
parent_window="",
|
||||
uri=uri,
|
||||
options=options,
|
||||
)
|
||||
|
||||
assert response
|
||||
assert response.response == 2
|
||||
|
||||
# Check the impl portal was called with the right args
|
||||
method_calls = mock_intf.GetMethodCalls("SetWallpaperURI")
|
||||
assert len(method_calls) == 0
|
||||
Reference in New Issue
Block a user