From 0ebb9ab05fe05e9d24aca14e8abb3a714ddb9848 Mon Sep 17 00:00:00 2001 From: Russell Martin Date: Wed, 19 Jun 2024 18:00:12 -0400 Subject: [PATCH 01/10] Run testbed with Wayland --- .github/workflows/ci.yml | 33 +++++++++++++++++++++++++++++++-- changes/2668.misc.rst | 1 + 2 files changed, 32 insertions(+), 2 deletions(-) create mode 100644 changes/2668.misc.rst diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d871bceb62..f85594d74f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -188,7 +188,7 @@ jobs: strategy: fail-fast: false matrix: - backend: [ "macOS-x86_64", "macOS-arm64", "windows", "linux", "android", "iOS" ] + backend: [ "macOS-x86_64", "macOS-arm64", "windows", "linux-x11", "linux-wayland","android", "iOS" ] include: - pre-command: "" briefcase-run-prefix: "" @@ -208,7 +208,7 @@ jobs: # We use a fixed Ubuntu version rather than `-latest` because at some point, # `-latest` will be updated, but it will be a soft changeover, which would cause # the system Python version to become inconsistent from run to run. - - backend: "linux" + - backend: "linux-x11" platform: "linux" runs-on: "ubuntu-22.04" # The package list should be the same as in tutorial-0.rst, and the BeeWare @@ -236,6 +236,35 @@ jobs: setup-python: false # Use the system Python packages app-user-data-path: "$HOME/.local/share/testbed" + - backend: "linux-wayland" + platform: "linux" + runs-on: "ubuntu-24.04" + # The package list should be the same as in tutorial-0.rst, and the BeeWare + # tutorial, plus mutter to provide a window manager, and libjpeg-dev for Pillow. + pre-command: | + sudo apt update -y + sudo apt install -y --no-install-recommends \ + mutter libjpeg-dev \ + pkg-config python3-dev libgirepository1.0-dev libcairo2-dev gir1.2-webkit2-4.1 + + # Start Virtual X server + echo "Start X server..." + Xvfb :99 -screen 0 2048x1536x24 & + sleep 1 + + # Start Window manager + echo "Start window manager..." + DISPLAY=:99 MUTTER_DEBUG_DUMMY_MODE_SPECS=2048x1536 \ + mutter --nested --wayland --no-x11 --wayland-display toga & + sleep 1 + + # Bypass guardrails for installing over system packages + export PIP_BREAK_SYSTEM_PACKAGES=1 + export PIP_IGNORE_INSTALLED=1 + briefcase-run-prefix: "WAYLAND_DISPLAY=toga" + setup-python: false # Use the system Python packages + app-user-data-path: "$HOME/.local/share/testbed" + - backend: "windows" platform: "windows" runs-on: "windows-latest" diff --git a/changes/2668.misc.rst b/changes/2668.misc.rst new file mode 100644 index 0000000000..d974f313bc --- /dev/null +++ b/changes/2668.misc.rst @@ -0,0 +1 @@ +Add Wayland to the matrix for testbed CI testing. From f6cce2360410f29369f09a242cf31fcd1ef339f2 Mon Sep 17 00:00:00 2001 From: Russell Martin Date: Thu, 20 Jun 2024 14:51:04 -0400 Subject: [PATCH 02/10] Update `test_current_window` for testbed on Wayland --- android/tests_backend/app.py | 3 +++ cocoa/tests_backend/app.py | 3 +++ gtk/tests_backend/app.py | 10 ++++++++++ gtk/tests_backend/probe.py | 4 ++++ gtk/tests_backend/screens.py | 8 +++----- iOS/tests_backend/app.py | 3 +++ testbed/tests/app/test_app.py | 36 +++++++++++++++++++---------------- winforms/tests_backend/app.py | 3 +++ 8 files changed, 49 insertions(+), 21 deletions(-) diff --git a/android/tests_backend/app.py b/android/tests_backend/app.py index f27cc8956d..56589312f2 100644 --- a/android/tests_backend/app.py +++ b/android/tests_backend/app.py @@ -38,6 +38,9 @@ def cache_path(self): def logs_path(self): return Path(self.get_app_context().getFilesDir().getPath()) / "log" + def assert_current_window(self, window): + assert self.app.current_window == window + def assert_app_icon(self, icon): xfail("Android apps don't have app icons at runtime") diff --git a/cocoa/tests_backend/app.py b/cocoa/tests_backend/app.py index 6fad33f81d..5489ac18df 100644 --- a/cocoa/tests_backend/app.py +++ b/cocoa/tests_backend/app.py @@ -60,6 +60,9 @@ def content_size(self, window): window.content._impl.native.frame.size.height, ) + def assert_current_window(self, window): + assert self.app.current_window == window + def assert_app_icon(self, icon): # We have no real way to check we've got the right icon; use pixel peeping as a # guess. Construct a PIL image from the current icon. diff --git a/gtk/tests_backend/app.py b/gtk/tests_backend/app.py index 37b3da5a7b..488f94dde0 100644 --- a/gtk/tests_backend/app.py +++ b/gtk/tests_backend/app.py @@ -48,6 +48,16 @@ def content_size(self, window): content_allocation = window._impl.container.get_allocation() return (content_allocation.width, content_allocation.height) + def assert_current_window(self, window): + # Gtk 3.24.41 ships with Ubuntu 24.04 where present() works on Wayland + if self.IS_WAYLAND and self.GTK_VERSION < (3, 24, 41): + pytest.skip( + f"Assigning the current window is not supported for " + f"Gtk {'.'.join(map(str, self.GTK_VERSION))} on Wayland" + ) + else: + assert self.app.current_window == window + def assert_app_icon(self, icon): for window in self.app.windows: # We have no real way to check we've got the right icon; use pixel peeping as a diff --git a/gtk/tests_backend/probe.py b/gtk/tests_backend/probe.py index 8c026917e7..9f510ead54 100644 --- a/gtk/tests_backend/probe.py +++ b/gtk/tests_backend/probe.py @@ -1,10 +1,14 @@ import asyncio +import os import toga from toga_gtk.libs import Gtk class BaseProbe: + GTK_VERSION = Gtk.MAJOR_VERSION, Gtk.MINOR_VERSION, Gtk.MICRO_VERSION + IS_WAYLAND = os.environ.get("WAYLAND_DISPLAY", "") != "" + def repaint_needed(self): return Gtk.events_pending() diff --git a/gtk/tests_backend/screens.py b/gtk/tests_backend/screens.py index 399544797c..a31e6b0518 100644 --- a/gtk/tests_backend/screens.py +++ b/gtk/tests_backend/screens.py @@ -1,5 +1,3 @@ -import os - import pytest from gi.repository import GdkX11 @@ -14,7 +12,7 @@ def __init__(self, screen): self.screen = screen self._impl = screen._impl self.native = screen._impl.native - if "WAYLAND_DISPLAY" in os.environ: + if self.IS_WAYLAND: # The native display type on Wayland is `__gi__.GdkWaylandMonitor` # However, that class can't be imported directly. pass @@ -22,7 +20,7 @@ def __init__(self, screen): assert isinstance(self.native, GdkX11.X11Monitor) def get_screenshot(self, format=TogaImage): - if "WAYLAND_DISPLAY" in os.environ: - pytest.skip("Screen.as_image() is not implemented on wayland.") + if self.IS_WAYLAND: + pytest.skip("Screen.as_image() is not implemented on Wayland.") else: return self.screen.as_image(format=format) diff --git a/iOS/tests_backend/app.py b/iOS/tests_backend/app.py index 430261cfbb..277c310659 100644 --- a/iOS/tests_backend/app.py +++ b/iOS/tests_backend/app.py @@ -44,6 +44,9 @@ def cache_path(self): def logs_path(self): return self.get_path(NSSearchPathDirectory.ApplicationSupport) / "Logs" + def assert_current_window(self, window): + assert self.app.current_window == window + def assert_app_icon(self, icon): pytest.xfail("iOS apps don't have app icons at runtime") diff --git a/testbed/tests/app/test_app.py b/testbed/tests/app/test_app.py index 6c8e85bcd1..017c7e2d76 100644 --- a/testbed/tests/app/test_app.py +++ b/testbed/tests/app/test_app.py @@ -37,14 +37,14 @@ async def test_full_screen(app): app.set_full_screen(app.current_window) app.exit_full_screen() - async def test_current_window(app, main_window, main_window_probe): + async def test_current_window(app, main_window, app_probe, main_window_probe): """The current window can be retrieved""" assert app.current_window == main_window # Explicitly set the current window app.current_window = main_window await main_window_probe.wait_for_window("Main window is still current") - assert app.current_window == main_window + app_probe.assert_current_window(main_window) async def test_app_lifecycle(app, app_probe): """Application lifecycle can be exercised""" @@ -153,12 +153,13 @@ async def test_menu_exit(monkeypatch, app, app_probe, mock_app_exit): mock_app_exit.assert_called_once_with() async def test_menu_close_windows(monkeypatch, app, app_probe, mock_app_exit): + window1 = toga.Window("Test Window 1", position=(150, 150), size=(200, 200)) + window2 = toga.Window("Test Window 2", position=(400, 150), size=(200, 200)) + window3 = toga.Window("Test Window 3", position=(300, 400), size=(200, 200)) + try: - window1 = toga.Window("Test Window 1", position=(150, 150), size=(200, 200)) window1.content = toga.Box(style=Pack(background_color=REBECCAPURPLE)) - window2 = toga.Window("Test Window 2", position=(400, 150), size=(200, 200)) window2.content = toga.Box(style=Pack(background_color=CORNFLOWERBLUE)) - window3 = toga.Window("Test Window 3", position=(300, 400), size=(200, 200)) window3.content = toga.Box(style=Pack(background_color=FIREBRICK)) window1.show() @@ -209,8 +210,9 @@ async def test_menu_close_windows(monkeypatch, app, app_probe, mock_app_exit): window3.close() async def test_menu_minimize(app, app_probe): + window1 = toga.Window("Test Window 1", position=(150, 150), size=(200, 200)) + try: - window1 = toga.Window("Test Window 1", position=(150, 150), size=(200, 200)) window1.content = toga.Box(style=Pack(background_color=REBECCAPURPLE)) window1.show() @@ -228,10 +230,11 @@ async def test_menu_minimize(app, app_probe): async def test_full_screen(app, app_probe): """Window can be made full screen""" + window1 = toga.Window("Test Window 1", position=(150, 150), size=(200, 200)) + window2 = toga.Window("Test Window 2", position=(400, 150), size=(200, 200)) + try: - window1 = toga.Window("Test Window 1", position=(150, 150), size=(200, 200)) window1.content = toga.Box(style=Pack(background_color=REBECCAPURPLE)) - window2 = toga.Window("Test Window 2", position=(400, 150), size=(200, 200)) window2.content = toga.Box(style=Pack(background_color=CORNFLOWERBLUE)) window1_probe = window_probe(app, window1) window2_probe = window_probe(app, window2) @@ -351,7 +354,7 @@ async def test_show_hide_cursor(app, app_probe): async def test_current_window(app, app_probe, main_window): """The current window can be retrieved.""" try: - assert app.current_window == main_window + app_probe.assert_current_window(main_window) # When all windows are hidden, WinForms and Cocoa return None, while GTK # returns the last active window. @@ -363,12 +366,13 @@ async def test_current_window(app, app_probe, main_window): finally: main_window.show() + window1 = toga.Window("Test Window 1", position=(150, 150), size=(200, 200)) + window2 = toga.Window("Test Window 2", position=(400, 150), size=(200, 200)) + window3 = toga.Window("Test Window 3", position=(300, 400), size=(200, 200)) + try: - window1 = toga.Window("Test Window 1", position=(150, 150), size=(200, 200)) window1.content = toga.Box(style=Pack(background_color=REBECCAPURPLE)) - window2 = toga.Window("Test Window 2", position=(400, 150), size=(200, 200)) window2.content = toga.Box(style=Pack(background_color=CORNFLOWERBLUE)) - window3 = toga.Window("Test Window 3", position=(300, 400), size=(200, 200)) window3.content = toga.Box(style=Pack(background_color=FIREBRICK)) # We don't need to probe anything window specific; we just need @@ -383,11 +387,11 @@ async def test_current_window(app, app_probe, main_window): app.current_window = window2 await window1_probe.wait_for_window("Window 2 is current") - assert app.current_window == window2 + app_probe.assert_current_window(window2) app.current_window = window3 await window1_probe.wait_for_window("Window 3 is current") - assert app.current_window == window3 + app_probe.assert_current_window(window3) # app_probe.platform tests? finally: @@ -511,8 +515,8 @@ async def test_menu_visit_homepage(monkeypatch, app, app_probe): """The visit homepage menu item can be used""" # If the backend defines a VISIT_HOMEPAGE command, mock the visit_homepage method, # and rebind the visit homepage command to the visit_homepage method. + visit_homepage = Mock() if toga.Command.VISIT_HOMEPAGE in app.commands: - visit_homepage = Mock() monkeypatch.setattr(app, "visit_homepage", visit_homepage) monkeypatch.setattr( app.commands[toga.Command.VISIT_HOMEPAGE], "_action", app.visit_homepage @@ -593,7 +597,7 @@ async def test_menu_items(app, app_probe): enabled=False, ) - # Dislble the items + # Disable the items app.disabled_cmd.enabled = False app.no_action_cmd.enabled = False diff --git a/winforms/tests_backend/app.py b/winforms/tests_backend/app.py index 84d2cd2f79..68484e593c 100644 --- a/winforms/tests_backend/app.py +++ b/winforms/tests_backend/app.py @@ -105,6 +105,9 @@ def is_full_screen(self, window): def content_size(self, window): return WindowProbe(self.app, window).content_size + def assert_current_window(self, window): + assert self.app.current_window == window + def assert_app_icon(self, icon): for window in self.app.windows: # We have no real way to check we've got the right icon; use pixel peeping as a From 05e8c0e15f06a8d453fe4ee290ce672dd6d867d9 Mon Sep 17 00:00:00 2001 From: Russell Martin Date: Thu, 20 Jun 2024 15:44:33 -0400 Subject: [PATCH 03/10] Fix window visibility and minimize testbed tests for Wayland --- android/tests_backend/window.py | 1 + cocoa/tests_backend/window.py | 2 ++ gtk/tests_backend/window.py | 2 ++ testbed/tests/window/test_window.py | 40 ++++++++++++++++++----------- winforms/tests_backend/window.py | 2 ++ 5 files changed, 32 insertions(+), 15 deletions(-) diff --git a/android/tests_backend/window.py b/android/tests_backend/window.py index 8b0d05c408..84dc654c0e 100644 --- a/android/tests_backend/window.py +++ b/android/tests_backend/window.py @@ -8,6 +8,7 @@ class WindowProbe(BaseProbe): def __init__(self, app, window): super().__init__(app) self.native = self.app._impl.native + self.window = window async def wait_for_window(self, message, minimize=False, full_screen=False): await self.redraw(message) diff --git a/cocoa/tests_backend/window.py b/cocoa/tests_backend/window.py index 7c1e41c50c..4dbcea9fd4 100644 --- a/cocoa/tests_backend/window.py +++ b/cocoa/tests_backend/window.py @@ -24,6 +24,8 @@ class WindowProbe(BaseProbe): supports_move_while_hidden = True supports_multiple_select_folder = True supports_unminimize = True + supports_minimize = True + supports_placement = True def __init__(self, app, window): super().__init__() diff --git a/gtk/tests_backend/window.py b/gtk/tests_backend/window.py index 28b1ffaf42..bf148a2e11 100644 --- a/gtk/tests_backend/window.py +++ b/gtk/tests_backend/window.py @@ -15,6 +15,8 @@ class WindowProbe(BaseProbe): supports_multiple_select_folder = True supports_move_while_hidden = False supports_unminimize = False + supports_minimize = not BaseProbe.IS_WAYLAND + supports_placement = not BaseProbe.IS_WAYLAND def __init__(self, app, window): super().__init__() diff --git a/testbed/tests/window/test_window.py b/testbed/tests/window/test_window.py index 70d20016e7..db9e9396a8 100644 --- a/testbed/tests/window/test_window.py +++ b/testbed/tests/window/test_window.py @@ -47,7 +47,7 @@ async def test_title(main_window, main_window_probe): await main_window_probe.wait_for_window("Window title can be reverted") -# Mobile platforms have different windowing characterics, so they have different tests. +# Mobile platforms have different windowing characteristics, so they have different tests. if toga.platform.current_platform in {"iOS", "android"}: #################################################################################### # Mobile platform tests @@ -92,9 +92,9 @@ async def test_move_and_resize(main_window, main_window_probe, capsys): assert main_window.size == initial_size assert main_window.position == (0, 0) - try: - orig_content = main_window.content + orig_content = main_window.content + try: box1 = toga.Box( style=Pack(background_color=REBECCAPURPLE, width=10, height=10) ) @@ -203,7 +203,8 @@ async def test_secondary_window_with_args(app, second_window, second_window_prob assert second_window.title == "Secondary Window" assert second_window.size == (300, 200) - assert second_window.position == (200, 300) + if second_window_probe.supports_placement: + assert second_window.position == (200, 300) second_window_probe.close() await second_window_probe.wait_for_window( @@ -231,10 +232,10 @@ async def test_secondary_window_with_content(app): label1 = toga.Label("Hello World") content = toga.Box(children=[label1]) - try: - window_with_content = toga.Window(content=content) - window_with_content_probe = window_probe(app, window_with_content) + window_with_content = toga.Window(content=content) + window_with_content_probe = window_probe(app, window_with_content) + try: window_with_content.show() await window_with_content_probe.wait_for_window( "Create a window with initial content" @@ -349,14 +350,16 @@ async def test_visibility(app, second_window, second_window_probe): assert second_window.visible assert second_window.size == (640, 480) - assert second_window.position == (200, 150) + if second_window_probe.supports_placement: + assert second_window.position == (200, 150) # Move the window second_window.position = (250, 200) await second_window_probe.wait_for_window("Secondary window has been moved") assert second_window.size == (640, 480) - assert second_window.position == (250, 200) + if second_window_probe.supports_placement: + assert second_window.position == (250, 200) # Resize the window second_window.size = (300, 250) @@ -386,7 +389,10 @@ async def test_visibility(app, second_window, second_window_probe): assert second_window.visible assert second_window.size == (250, 200) - if second_window_probe.supports_move_while_hidden: + if ( + second_window_probe.supports_move_while_hidden + and second_window_probe.supports_placement + ): assert second_window.position == (300, 150) second_window_probe.minimize() @@ -396,7 +402,8 @@ async def test_visibility(app, second_window, second_window_probe): minimize=True, ) - assert second_window_probe.is_minimized + if second_window_probe.supports_minimize: + assert second_window_probe.is_minimized if second_window_probe.supports_unminimize: second_window_probe.unminimize() @@ -426,7 +433,8 @@ async def test_move_and_resize(second_window, second_window_probe): second_window.position = (150, 50) await second_window_probe.wait_for_window("Secondary window has been moved") - assert second_window.position == (150, 50) + if second_window_probe.supports_placement: + assert second_window.position == (150, 50) second_window.size = (200, 150) await second_window_probe.wait_for_window("Secondary window has been resized") @@ -521,11 +529,13 @@ async def test_screen(second_window, second_window_probe): # Move the window using absolute position. second_window.position = (200, 200) await second_window_probe.wait_for_window("Secondary window has been moved") - assert second_window.position != initial_position + if second_window_probe.supports_placement: + assert second_window.position != initial_position # `position` and `screen_position` will be same as the window will be in primary screen. - assert second_window.position == (200, 200) - assert second_window.screen_position == (200, 200) + if second_window_probe.supports_placement: + assert second_window.position == (200, 200) + assert second_window.screen_position == (200, 200) # Move the window between available screens and assert its `screen_position` for screen in second_window.app.screens: diff --git a/winforms/tests_backend/window.py b/winforms/tests_backend/window.py index 55d6c4d63c..8c32c51b4d 100644 --- a/winforms/tests_backend/window.py +++ b/winforms/tests_backend/window.py @@ -23,6 +23,8 @@ class WindowProbe(BaseProbe): supports_move_while_hidden = True supports_multiple_select_folder = False supports_unminimize = True + supports_minimize = True + supports_placement = True def __init__(self, app, window): super().__init__() From 10e9e65d74b29bb281ad14b3c8c054554164f55f Mon Sep 17 00:00:00 2001 From: Russell Martin Date: Thu, 20 Jun 2024 16:26:57 -0400 Subject: [PATCH 04/10] Test Wayland on Ubuntu 22.04 --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f85594d74f..b0c1c98ef9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -238,7 +238,7 @@ jobs: - backend: "linux-wayland" platform: "linux" - runs-on: "ubuntu-24.04" + runs-on: "ubuntu-22.04" # The package list should be the same as in tutorial-0.rst, and the BeeWare # tutorial, plus mutter to provide a window manager, and libjpeg-dev for Pillow. pre-command: | From e51310fb7ec5232405fcfebf26fadcf3a9004979 Mon Sep 17 00:00:00 2001 From: Russell Martin Date: Thu, 20 Jun 2024 16:50:38 -0400 Subject: [PATCH 05/10] Fix canvas testbed tests for Wayland --- gtk/tests_backend/widgets/canvas.py | 7 ++++++- .../canvas/multiline_text-gtk-wayland.png | Bin 0 -> 9025 bytes 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 testbed/src/testbed/resources/canvas/multiline_text-gtk-wayland.png diff --git a/gtk/tests_backend/widgets/canvas.py b/gtk/tests_backend/widgets/canvas.py index 81deb6caa9..b27a630024 100644 --- a/gtk/tests_backend/widgets/canvas.py +++ b/gtk/tests_backend/widgets/canvas.py @@ -11,7 +11,12 @@ class CanvasProbe(SimpleProbe): native_class = Gtk.DrawingArea def reference_variant(self, reference): - if reference in {"write_text", "multiline_text"}: + if reference == "multiline_text": + if self.IS_WAYLAND: + return f"{reference}-gtk-wayland" + else: + return f"{reference}-gtk" + elif reference == "write_text": return f"{reference}-gtk" else: return reference diff --git a/testbed/src/testbed/resources/canvas/multiline_text-gtk-wayland.png b/testbed/src/testbed/resources/canvas/multiline_text-gtk-wayland.png new file mode 100644 index 0000000000000000000000000000000000000000..ad2503ac6c5ea303c47cba3da2b55c0528d458d1 GIT binary patch literal 9025 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 UwaIAs(-=fSR^>s-eIx(>15iw!EdT%j literal 0 HcmV?d00001 From 150d32ae453404692570112e893ad0f88ad238f2 Mon Sep 17 00:00:00 2001 From: Russell Martin Date: Fri, 21 Jun 2024 08:16:02 -0400 Subject: [PATCH 06/10] Fix testbed coverage for Wayland on Linux --- gtk/src/toga_gtk/app.py | 6 +++--- gtk/src/toga_gtk/screens.py | 4 ++-- testbed/pyproject.toml | 1 + testbed/tests/testbed.py | 8 ++++++++ 4 files changed, 14 insertions(+), 5 deletions(-) diff --git a/gtk/src/toga_gtk/app.py b/gtk/src/toga_gtk/app.py index 11aad1ca9c..96a0934993 100644 --- a/gtk/src/toga_gtk/app.py +++ b/gtk/src/toga_gtk/app.py @@ -192,13 +192,13 @@ def set_main_window(self, window): def get_screens(self): display = Gdk.Display.get_default() - if "WAYLAND_DISPLAY" in os.environ: # pragma: no cover + if "WAYLAND_DISPLAY" in os.environ: # pragma: no-cover-if-linux-x # `get_primary_monitor()` doesn't work on wayland, so return as it is. return [ ScreenImpl(native=display.get_monitor(i)) for i in range(display.get_n_monitors()) ] - else: + else: # pragma: no-cover-if-linux-wayland primary_screen = ScreenImpl(display.get_primary_monitor()) screen_list = [primary_screen] + [ ScreenImpl(native=display.get_monitor(i)) @@ -252,7 +252,7 @@ def show_cursor(self): # Window control ###################################################################### - def get_current_window(self): + def get_current_window(self): # pragma: no-cover-if-linux-wayland current_window = self.native.get_active_window()._impl return current_window if current_window.interface.visible else None diff --git a/gtk/src/toga_gtk/screens.py b/gtk/src/toga_gtk/screens.py index f16ea792eb..1ef6f7a98a 100644 --- a/gtk/src/toga_gtk/screens.py +++ b/gtk/src/toga_gtk/screens.py @@ -31,10 +31,10 @@ def get_size(self) -> Size: return Size(geometry.width, geometry.height) def get_image_data(self): - if "WAYLAND_DISPLAY" in os.environ: + if "WAYLAND_DISPLAY" in os.environ: # pragma: no cover # Not implemented on wayland due to wayland security policies. self.interface.factory.not_implemented("Screen.get_image_data() on Wayland") - else: + else: # pragma: no-cover-if-linux-wayland # Only works for Xorg display = self.native.get_display() screen = display.get_default_screen() diff --git a/testbed/pyproject.toml b/testbed/pyproject.toml index b0a3ce4c07..d3a4a7faaf 100644 --- a/testbed/pyproject.toml +++ b/testbed/pyproject.toml @@ -5,6 +5,7 @@ version = "0.0.1" [project.optional-dependencies] test = [ "coverage==7.5.3", + "coverage-conditional-plugin == 0.9.0", # fonttools is only needed by Android, but we need to use # sys.platform == 'linux' as there's no dependency identifier # that can target Android exclusively until 3.13 lands. diff --git a/testbed/tests/testbed.py b/testbed/tests/testbed.py index 01dcf24363..00a6858ead 100644 --- a/testbed/tests/testbed.py +++ b/testbed/tests/testbed.py @@ -115,6 +115,14 @@ def run_tests(app, cov, args, report_coverage, run_slow): branch=True, source_pkgs=[toga_backend], ) + cov.set_option("run:plugins", ["coverage_conditional_plugin"]) + cov.set_option( + "coverage_conditional_plugin:rules", + { + "no-cover-if-linux-wayland": "os_environ.get('WAYLAND_DISPLAY') != ''", + "no-cover-if-linux-x": "os_environ.get('WAYLAND_DISPLAY', 'not-set') == 'not-set'", + }, + ) cov.start() # Create the test app, starting the test suite as a background task From 4d5ac2693c859a54f300b53f1a58405d1a8b6d0f Mon Sep 17 00:00:00 2001 From: Russell Martin Date: Fri, 21 Jun 2024 10:46:39 -0400 Subject: [PATCH 07/10] Allow canvas test `test_multiline_text` to fail outside CI --- .github/workflows/ci.yml | 4 ++-- testbed/tests/testbed.py | 11 ++++++++++- testbed/tests/widgets/test_canvas.py | 5 +++++ 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b0c1c98ef9..be3f710f3d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -287,7 +287,7 @@ jobs: platform: "android" runs-on: "ubuntu-latest" briefcase-run-prefix: JAVA_HOME=${JAVA_HOME_17_X64} - briefcase-run-args: > + briefcase-run-args: >- --device '{"avd":"beePhone","skin":"pixel_3a"}' --Xemulator=-no-window --Xemulator=-no-snapshot @@ -333,7 +333,7 @@ jobs: timeout-minutes: 15 run: | ${{ matrix.briefcase-run-prefix }} \ - briefcase run ${{ matrix.platform }} --log --test ${{ matrix.briefcase-run-args }} + briefcase run ${{ matrix.platform }} --log --test ${{ matrix.briefcase-run-args }} -- --ci - name: Upload Logs uses: actions/upload-artifact@v4.3.3 diff --git a/testbed/tests/testbed.py b/testbed/tests/testbed.py index 00a6858ead..de96b7137b 100644 --- a/testbed/tests/testbed.py +++ b/testbed/tests/testbed.py @@ -13,7 +13,7 @@ from testbed.app import main -def run_tests(app, cov, args, report_coverage, run_slow): +def run_tests(app, cov, args, report_coverage, run_slow, running_in_ci): try: # Wait for the app's main window to be visible. print("Waiting for app to be ready for testing... ", end="", flush=True) @@ -26,6 +26,8 @@ def run_tests(app, cov, args, report_coverage, run_slow): project_path = Path(__file__).parent.parent os.chdir(project_path) + os.environ["RUNNING_IN_CI"] = "true" if running_in_ci else "" + app.returncode = pytest.main( [ # Output formatting @@ -146,6 +148,12 @@ def run_tests(app, cov, args, report_coverage, run_slow): except ValueError: report_coverage = False + try: + args.remove("--ci") + running_in_ci = True + except ValueError: + running_in_ci = False + # If there are no other specified arguments, default to running the whole suite, # and reporting coverage. if len(args) == 0: @@ -160,6 +168,7 @@ def run_tests(app, cov, args, report_coverage, run_slow): args=args, run_slow=run_slow, report_coverage=report_coverage, + running_in_ci=running_in_ci, ) ) # Queue a background task to run that will start the main thread. We do this, diff --git a/testbed/tests/widgets/test_canvas.py b/testbed/tests/widgets/test_canvas.py index e351ed1364..3ac2878766 100644 --- a/testbed/tests/widgets/test_canvas.py +++ b/testbed/tests/widgets/test_canvas.py @@ -1,4 +1,5 @@ import math +import os from math import pi, radians from unittest.mock import Mock, call @@ -693,6 +694,10 @@ async def test_write_text(canvas, probe): assert_reference(probe, "write_text", threshold=0.07) +@pytest.mark.xfail( + condition=os.environ.get("RUNNING_IN_CI") != "true", + reason="may fail outside of CI", +) async def test_multiline_text(canvas, probe): "Multiline text can be measured and written" From ff60b144b901a84cda5e61b7b4cd4ab095f88be7 Mon Sep 17 00:00:00 2001 From: Russell Martin Date: Fri, 21 Jun 2024 15:19:53 -0400 Subject: [PATCH 08/10] Minor cleanups for Wayland testbed testing --- .github/workflows/ci.yml | 17 +++++++---------- testbed/tests/testbed.py | 2 +- testbed/tests/widgets/test_canvas.py | 2 +- 3 files changed, 9 insertions(+), 12 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index be3f710f3d..0354ce380f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -223,12 +223,12 @@ jobs: sudo apt install -y --no-install-recommends \ blackbox pkg-config python3-dev libgirepository1.0-dev libcairo2-dev gir1.2-webkit2-4.1 - # Start Virtual X server + # Start Virtual X Server echo "Start X server..." Xvfb :99 -screen 0 2048x1536x24 & sleep 1 - # Start Window manager + # Start Window Mmanager echo "Start window manager..." DISPLAY=:99 blackbox & sleep 1 @@ -244,23 +244,20 @@ jobs: pre-command: | sudo apt update -y sudo apt install -y --no-install-recommends \ - mutter libjpeg-dev \ - pkg-config python3-dev libgirepository1.0-dev libcairo2-dev gir1.2-webkit2-4.1 + mutter pkg-config python3-dev libgirepository1.0-dev libcairo2-dev gir1.2-webkit2-4.1 - # Start Virtual X server + # Start Virtual X Server echo "Start X server..." Xvfb :99 -screen 0 2048x1536x24 & sleep 1 - # Start Window manager + # Start Window Manager echo "Start window manager..." + # mutter is being run inside a virtual X server because mutter's headless + # mode is not compatible with Gtk DISPLAY=:99 MUTTER_DEBUG_DUMMY_MODE_SPECS=2048x1536 \ mutter --nested --wayland --no-x11 --wayland-display toga & sleep 1 - - # Bypass guardrails for installing over system packages - export PIP_BREAK_SYSTEM_PACKAGES=1 - export PIP_IGNORE_INSTALLED=1 briefcase-run-prefix: "WAYLAND_DISPLAY=toga" setup-python: false # Use the system Python packages app-user-data-path: "$HOME/.local/share/testbed" diff --git a/testbed/tests/testbed.py b/testbed/tests/testbed.py index de96b7137b..73e2067fb9 100644 --- a/testbed/tests/testbed.py +++ b/testbed/tests/testbed.py @@ -121,7 +121,7 @@ def run_tests(app, cov, args, report_coverage, run_slow, running_in_ci): cov.set_option( "coverage_conditional_plugin:rules", { - "no-cover-if-linux-wayland": "os_environ.get('WAYLAND_DISPLAY') != ''", + "no-cover-if-linux-wayland": "os_environ.get('WAYLAND_DISPLAY', '') != ''", "no-cover-if-linux-x": "os_environ.get('WAYLAND_DISPLAY', 'not-set') == 'not-set'", }, ) diff --git a/testbed/tests/widgets/test_canvas.py b/testbed/tests/widgets/test_canvas.py index 3ac2878766..a5c4e8c4b9 100644 --- a/testbed/tests/widgets/test_canvas.py +++ b/testbed/tests/widgets/test_canvas.py @@ -696,7 +696,7 @@ async def test_write_text(canvas, probe): @pytest.mark.xfail( condition=os.environ.get("RUNNING_IN_CI") != "true", - reason="may fail outside of CI", + reason="may fail outside of a GitHub runner environment", ) async def test_multiline_text(canvas, probe): "Multiline text can be measured and written" From a1df8d403ab4d24ad7b73904aaab4de152b863a2 Mon Sep 17 00:00:00 2001 From: Russell Martin Date: Sat, 22 Jun 2024 11:22:52 -0400 Subject: [PATCH 09/10] Simplify abstracted current window assertion in testbed --- android/tests_backend/app.py | 3 --- cocoa/tests_backend/app.py | 4 +--- gtk/tests_backend/app.py | 14 ++++---------- gtk/tests_backend/window.py | 1 + iOS/tests_backend/app.py | 3 --- testbed/tests/app/test_app.py | 11 +++++++---- winforms/tests_backend/app.py | 4 +--- 7 files changed, 14 insertions(+), 26 deletions(-) diff --git a/android/tests_backend/app.py b/android/tests_backend/app.py index 56589312f2..f27cc8956d 100644 --- a/android/tests_backend/app.py +++ b/android/tests_backend/app.py @@ -38,9 +38,6 @@ def cache_path(self): def logs_path(self): return Path(self.get_app_context().getFilesDir().getPath()) / "log" - def assert_current_window(self, window): - assert self.app.current_window == window - def assert_app_icon(self, icon): xfail("Android apps don't have app icons at runtime") diff --git a/cocoa/tests_backend/app.py b/cocoa/tests_backend/app.py index 5489ac18df..2719a5d1ba 100644 --- a/cocoa/tests_backend/app.py +++ b/cocoa/tests_backend/app.py @@ -21,6 +21,7 @@ class AppProbe(BaseProbe): supports_key = True supports_key_mod3 = True + supports_current_window_assignment = True def __init__(self, app): super().__init__() @@ -60,9 +61,6 @@ def content_size(self, window): window.content._impl.native.frame.size.height, ) - def assert_current_window(self, window): - assert self.app.current_window == window - def assert_app_icon(self, icon): # We have no real way to check we've got the right icon; use pixel peeping as a # guess. Construct a PIL image from the current icon. diff --git a/gtk/tests_backend/app.py b/gtk/tests_backend/app.py index 488f94dde0..f18003857c 100644 --- a/gtk/tests_backend/app.py +++ b/gtk/tests_backend/app.py @@ -13,6 +13,10 @@ class AppProbe(BaseProbe): supports_key = True supports_key_mod3 = True + # Gtk 3.24.41 ships with Ubuntu 24.04 where present() works on Wayland + supports_current_window_assignment = not ( + BaseProbe.IS_WAYLAND and BaseProbe.GTK_VERSION < (3, 24, 41) + ) def __init__(self, app): super().__init__() @@ -48,16 +52,6 @@ def content_size(self, window): content_allocation = window._impl.container.get_allocation() return (content_allocation.width, content_allocation.height) - def assert_current_window(self, window): - # Gtk 3.24.41 ships with Ubuntu 24.04 where present() works on Wayland - if self.IS_WAYLAND and self.GTK_VERSION < (3, 24, 41): - pytest.skip( - f"Assigning the current window is not supported for " - f"Gtk {'.'.join(map(str, self.GTK_VERSION))} on Wayland" - ) - else: - assert self.app.current_window == window - def assert_app_icon(self, icon): for window in self.app.windows: # We have no real way to check we've got the right icon; use pixel peeping as a diff --git a/gtk/tests_backend/window.py b/gtk/tests_backend/window.py index bf148a2e11..0abda6b85e 100644 --- a/gtk/tests_backend/window.py +++ b/gtk/tests_backend/window.py @@ -15,6 +15,7 @@ class WindowProbe(BaseProbe): supports_multiple_select_folder = True supports_move_while_hidden = False supports_unminimize = False + # Wayland mostly prohibits interaction with the larger windowing environment supports_minimize = not BaseProbe.IS_WAYLAND supports_placement = not BaseProbe.IS_WAYLAND diff --git a/iOS/tests_backend/app.py b/iOS/tests_backend/app.py index 277c310659..430261cfbb 100644 --- a/iOS/tests_backend/app.py +++ b/iOS/tests_backend/app.py @@ -44,9 +44,6 @@ def cache_path(self): def logs_path(self): return self.get_path(NSSearchPathDirectory.ApplicationSupport) / "Logs" - def assert_current_window(self, window): - assert self.app.current_window == window - def assert_app_icon(self, icon): pytest.xfail("iOS apps don't have app icons at runtime") diff --git a/testbed/tests/app/test_app.py b/testbed/tests/app/test_app.py index 017c7e2d76..3b897a9190 100644 --- a/testbed/tests/app/test_app.py +++ b/testbed/tests/app/test_app.py @@ -44,7 +44,7 @@ async def test_current_window(app, main_window, app_probe, main_window_probe): # Explicitly set the current window app.current_window = main_window await main_window_probe.wait_for_window("Main window is still current") - app_probe.assert_current_window(main_window) + assert app.current_window == main_window async def test_app_lifecycle(app, app_probe): """Application lifecycle can be exercised""" @@ -354,7 +354,8 @@ async def test_show_hide_cursor(app, app_probe): async def test_current_window(app, app_probe, main_window): """The current window can be retrieved.""" try: - app_probe.assert_current_window(main_window) + if app_probe.supports_current_window_assignment: + assert app.current_window == main_window # When all windows are hidden, WinForms and Cocoa return None, while GTK # returns the last active window. @@ -387,11 +388,13 @@ async def test_current_window(app, app_probe, main_window): app.current_window = window2 await window1_probe.wait_for_window("Window 2 is current") - app_probe.assert_current_window(window2) + if app_probe.supports_current_window_assignment: + assert app.current_window == window2 app.current_window = window3 await window1_probe.wait_for_window("Window 3 is current") - app_probe.assert_current_window(window3) + if app_probe.supports_current_window_assignment: + assert app.current_window == window3 # app_probe.platform tests? finally: diff --git a/winforms/tests_backend/app.py b/winforms/tests_backend/app.py index 68484e593c..932a9bf534 100644 --- a/winforms/tests_backend/app.py +++ b/winforms/tests_backend/app.py @@ -18,6 +18,7 @@ class AppProbe(BaseProbe): supports_key = True supports_key_mod3 = False + supports_current_window_assignment = True def __init__(self, app): super().__init__() @@ -105,9 +106,6 @@ def is_full_screen(self, window): def content_size(self, window): return WindowProbe(self.app, window).content_size - def assert_current_window(self, window): - assert self.app.current_window == window - def assert_app_icon(self, icon): for window in self.app.windows: # We have no real way to check we've got the right icon; use pixel peeping as a From ca8a068248954bbb11378e692c16d7e19171e044 Mon Sep 17 00:00:00 2001 From: Russell Martin Date: Sat, 22 Jun 2024 12:28:41 -0400 Subject: [PATCH 10/10] Label original Linux multiline_text reference image as x11 --- gtk/tests_backend/widgets/canvas.py | 2 +- ...line_text-gtk.png => multiline_text-gtk-x11.png} | Bin 2 files changed, 1 insertion(+), 1 deletion(-) rename testbed/src/testbed/resources/canvas/{multiline_text-gtk.png => multiline_text-gtk-x11.png} (100%) diff --git a/gtk/tests_backend/widgets/canvas.py b/gtk/tests_backend/widgets/canvas.py index b27a630024..1d00c6649a 100644 --- a/gtk/tests_backend/widgets/canvas.py +++ b/gtk/tests_backend/widgets/canvas.py @@ -15,7 +15,7 @@ def reference_variant(self, reference): if self.IS_WAYLAND: return f"{reference}-gtk-wayland" else: - return f"{reference}-gtk" + return f"{reference}-gtk-x11" elif reference == "write_text": return f"{reference}-gtk" else: diff --git a/testbed/src/testbed/resources/canvas/multiline_text-gtk.png b/testbed/src/testbed/resources/canvas/multiline_text-gtk-x11.png similarity index 100% rename from testbed/src/testbed/resources/canvas/multiline_text-gtk.png rename to testbed/src/testbed/resources/canvas/multiline_text-gtk-x11.png