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

Add polling based window waiting for testbed #3047

Merged
merged 13 commits into from
Dec 23, 2024
33 changes: 28 additions & 5 deletions android/tests_backend/window.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import asyncio

from android.content import Context
from androidx.appcompat import R as appcompat_R

from toga.constants import WindowState

from .dialogs import DialogsMixin
from .probe import BaseProbe

Expand All @@ -20,12 +24,31 @@ async def wait_for_window(
message,
minimize=False,
full_screen=False,
state_switch_not_from_normal=False,
expected_state=None,
):
await self.redraw(
message,
delay=(0.5 if (full_screen or state_switch_not_from_normal) else 0.1),
)
await self.redraw(message, delay=0.1)
if expected_state:
timeout = 5
polling_interval = 0.1
exception = None
loop = asyncio.get_running_loop()
start_time = loop.time()
while (loop.time() - start_time) < timeout:
try:
assert self.instantaneous_state == expected_state
return
except AssertionError as e:
exception = e
await asyncio.sleep(polling_interval)
continue
raise exception

async def wait_for_window_close(self, pre_close_window_state):
if pre_close_window_state == WindowState.FULLSCREEN:
delay = 0.5
else:
delay = 0.1
await self.redraw("Closing window", delay=delay)

@property
def content_size(self):
Expand Down
1 change: 1 addition & 0 deletions changes/3047.misc.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
The `wait_for_window()` window probe method, now has a polling based waiting mechanism.
39 changes: 30 additions & 9 deletions cocoa/tests_backend/window.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import asyncio

from rubicon.objc import objc_id, send_message

from toga.constants import WindowState
from toga_cocoa.libs import NSWindow, NSWindowStyleMask

from .dialogs import DialogsMixin
Expand Down Expand Up @@ -27,16 +30,34 @@ async def wait_for_window(
message,
minimize=False,
full_screen=False,
state_switch_not_from_normal=False,
expected_state=None,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we still using the minimize and full_screen arguments? If so... why? My original question about this was "What's the difference between full_screen=True and expected_state=FULL_SCREEN" - if the answer is "nothing", then why do we need the other arguments?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like, I had missed cleanup of unused parameters. I have removed them now.

):
await self.redraw(
message,
delay=(
1.75
if state_switch_not_from_normal
else 0.75 if full_screen else 0.5 if minimize else 0.1
),
)
await self.redraw(message, delay=0.1)
if expected_state:
timeout = 5
polling_interval = 0.1
exception = None
loop = asyncio.get_running_loop()
start_time = loop.time()
while (loop.time() - start_time) < timeout:
try:
assert self.instantaneous_state == expected_state
assert self.window._impl._pending_state_transition is None
return
except AssertionError as e:
exception = e
await asyncio.sleep(polling_interval)
continue
raise exception

async def wait_for_window_close(self, pre_close_window_state):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't pre_close_window_state be determined from the window itself?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Once, window.close() is called, then determining the window state would be unreliable. To avoid passing the parameter, we could determine the pre_close_window_state and close the window at the window probe.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should I change wait_for_window_close() to close_and_wait_for_window(), so that we could determine the pre_close_window_state and close the window at the window probe?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When you start proposing method names like close_and_wait_for_window(), you should be stepping back and asking "is there a better name for this?". In this case - it's literally a cleanup method.. so... window_probe.cleanup() (doing both the close and wait) would make sense to me.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the subject of naming, expected_state is also fairly verbose - unless you're expecting a different state to be passed into the window, state is fine as an argument name.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have added a window_probe.cleanup() method and replaced expected_state with state.

if pre_close_window_state == WindowState.FULLSCREEN:
delay = 1
elif pre_close_window_state == WindowState.MINIMIZED:
delay = 0.5
else:
delay = 0.1
await self.redraw("Closing window", delay=delay)

def close(self):
self.native.performClose(None)
Expand Down
33 changes: 28 additions & 5 deletions gtk/tests_backend/window.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import asyncio

from toga.constants import WindowState
from toga_gtk.libs import IS_WAYLAND, Gdk, Gtk

from .dialogs import DialogsMixin
Expand Down Expand Up @@ -29,12 +32,32 @@ async def wait_for_window(
message,
minimize=False,
full_screen=False,
state_switch_not_from_normal=False,
expected_state=None,
):
await self.redraw(
message,
delay=(0.5 if (full_screen or minimize) else 0.1),
)
await self.redraw(message, delay=0.1)
if expected_state:
timeout = 5
polling_interval = 0.1
exception = None
loop = asyncio.get_running_loop()
start_time = loop.time()
while (loop.time() - start_time) < timeout:
try:
assert self.instantaneous_state == expected_state
assert self.window._impl._pending_state_transition is None
return
except AssertionError as e:
exception = e
await asyncio.sleep(polling_interval)
continue
raise exception

async def wait_for_window_close(self, pre_close_window_state):
if pre_close_window_state in {WindowState.FULLSCREEN, WindowState.MINIMIZED}:
delay = 0.5
else:
delay = 0.1
await self.redraw("Closing window", delay=delay)

def close(self):
if self.is_closable:
Expand Down
22 changes: 21 additions & 1 deletion iOS/tests_backend/window.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import asyncio

import pytest

from toga_iOS.libs import UIApplication, UIWindow
Expand All @@ -23,9 +25,27 @@ async def wait_for_window(
message,
minimize=False,
full_screen=False,
state_switch_not_from_normal=False,
expected_state=None,
):
await self.redraw(message)
if expected_state:
timeout = 5
polling_interval = 0.1
exception = None
loop = asyncio.get_running_loop()
start_time = loop.time()
while (loop.time() - start_time) < timeout:
try:
assert self.instantaneous_state == expected_state
return
except AssertionError as e:
exception = e
await asyncio.sleep(polling_interval)
continue
raise exception

async def wait_for_window_close(self, pre_close_window_state):
await self.redraw("Closing window")

@property
def content_size(self):
Expand Down
Loading
Loading