Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow using the kiosk on platforms without DBus #159

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,21 @@ jobs:
- uses: DeterminateSystems/magic-nix-cache-action@v4
- run: cd kiosk && nix-shell --run bin/test

kiosk-platform-smoke-test:
# While the smoke test should run on other platforms, it does not currently
# run on GitHub's Ubuntu runners, as they don't support GUI applications.
runs-on: macos-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
with:
python-version: '3.8'
- uses: cachix/install-nix-action@v18
with:
nix_path: nixpkgs=channel:nixos-unstable
- name: Smoke Test
run: cd kiosk && nix-shell --run "python test/platform-smoke.py"

build-vm:
runs-on: ubuntu-latest
steps:
Expand Down
6 changes: 6 additions & 0 deletions kiosk/Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,9 @@ Then, point a Chromium-based browser to `http://127.0.0.1:3355`.

Additional documentation is available at:
https://doc.qt.io/qt-6/qtwebengine-debugging.html

## Supported platforms

The kiosk is written with use within PlayOS in mind (implying connman and DBus as part of the system). To allow for testing web pages in the kiosk on developer machines, macOS is also supported, with the following limitations:

- No proxy server support
5 changes: 0 additions & 5 deletions kiosk/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,5 @@ python3Packages.buildPythonApplication rec {
shellHook = ''
# Give access to kiosk_browser module
export PYTHONPATH=./:$PYTHONPATH

# Setup Qt environment
bashdir=$(mktemp -d)
makeWrapper "$(type -p bash)" "$bashdir/bash" "''${qtWrapperArgs[@]}"
exec "$bashdir/bash"
'';
}
2 changes: 1 addition & 1 deletion kiosk/kiosk_browser/main_widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ def __init__(self, kiosk_url: str, settings_url: str, toggle_settings_key: str):
super(MainWidget, self).__init__()

# Proxy
proxy = proxy_module.Proxy()
proxy = proxy_module.init()
proxy.start_monitoring_daemon()

# Browser widget
Expand Down
44 changes: 40 additions & 4 deletions kiosk/kiosk_browser/proxy.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import collections
import dbus
import logging
import platform
import threading
import urllib
from PyQt6.QtNetwork import QNetworkProxy
Expand Down Expand Up @@ -115,9 +116,47 @@ def set_no_proxy_in_qt_app():
logging.info(f"Set no proxy in Qt application")
QNetworkProxy.setApplicationProxy(QNetworkProxy())

class Proxy():
def init():
"""Initialize a suitable Proxy instance for the current platform."""
if platform.system() in ['Linux']:
return DBusProxy()
else:
return Proxy()

class Proxy:
"""Base class for proxy querying.

The base class does not know how to query for proxy information and may be used as a fallback that always reports that no proxy is configured.
"""
_proxy: ProxyConf | None

# For the base class, this is a pass, not knowing how to monitor in the general case.
def start_monitoring_daemon(self) -> None:
"""Start a daemon monitoring for proxy changes.

In the base class, no monitoring method is known, and starting a daemon is skipped.
"""
pass

def get_current(self) -> ProxyConf | None:
"""Get the currently configured proxy.

This is always `None` in the base class.
"""
return self._proxy

class DBusProxy(Proxy):
"""A Proxy class for DBus/Linux systems.

This class assumes that connman is the network manager and that it can be queried via DBus.
"""

_proxy: ProxyConf | None
_bus: dbus.SystemBus

def __init__(self):
super().__init__()

DBusGMainLoop(set_as_default=True)
self._bus = dbus.SystemBus()
self._proxy = get_current_proxy(self._bus)
Expand All @@ -129,9 +168,6 @@ def start_monitoring_daemon(self):
thread.daemon = True
thread.start()

def get_current(self):
return self._proxy

def _monitor(self):
self._bus.add_signal_receiver(
handler_function = self._on_property_changed,
Expand Down
65 changes: 65 additions & 0 deletions kiosk/test/platform-smoke.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import http.server
import socket
import socketserver
import threading
import subprocess
import time

PORT = 45678
request_count = 0

def main():
"""Checks that the kiosk-browser does not crash immediately on start and makes a request to the provided URL.

This serves as a minimal test to make sure the kiosk can run on different platforms.
"""

# Start dummy web server in thread, with signal to stop
keep_running = threading.Event()
keep_running.set()
server_thread = threading.Thread(target=run_server, args=(keep_running,))
server_thread.start()
time.sleep(1)

try:
browser_process = subprocess.Popen(['bin/kiosk-browser', f'http://localhost:{PORT}', f'http://localhost:{PORT}'])
time.sleep(5)

# Minimal expectations
assert browser_process.poll() is None, "Browser process has crashed."
assert request_count >= 1, "No requests were made to the server."

print("Smoke test passed successfully.")

finally:
# Send signal to stop web server and wait for completion
keep_running.clear()
server_thread.join()

# Terminate the browser process
browser_process.terminate()

class RequestHandler(http.server.SimpleHTTPRequestHandler):
"""Default request handler with added counter."""
def do_GET(self):
global request_count
request_count += 1
# Call superclass method to actually serve the request
super().do_GET()

def run_server(keep_running):
"""Run the web server, checking whether to keep running frequently."""
with socketserver.TCPServer(("", PORT), RequestHandler, bind_and_activate=False) as httpd:
# Let address be reused to avoid failure on repeated runs
httpd.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
httpd.server_bind()
httpd.server_activate()
# Timeout frequently to check if killed
httpd.timeout = 0.5
try:
while keep_running.is_set():
httpd.handle_request()
finally:
httpd.server_close()

main()
Loading