Testbed presentation mode test failure on macOS
Describe the bug
I'm consistently getting the same reported failure for tests/app/test_desktop.py::test_presentation_mode, though it doesn't always look the same, visually. Sometimes the testbed app exits normally... but sometimes both my monitors turn all black and stay that way. Unplugging one and plugging it back in puts things back to normal, but nothing short of that seems to. I've confirmed none of these work:
- Command + tab
- Escape
- Command + Option + Escape to try and open Force Quit
- Fn + F or Cmd + Ctrl + F to try and exit full screen
- Frantic mouse-clicking
Other things are still running normally on my computer, despite the black screens — music and sounds still play, and if I run the whole testbed, the rest of the tests run and pass as usual; I just can't see the results until I unplug/plug a monitor.
Steps to reproduce
briefcase run --test -- tests/app/test_desktop.py::test_presentation_mode (or dev)
Expected behavior
I mean ideally the test would pass, but even if it fails, hopefully it shouldn't lock up both my monitors.
Screenshots
No response
Environment
- Operating System: macOS Sonoma 14.7.3
- Python version: 3.13.0
- Software versions:
- Briefcase: 0.3.22
- Toga: Main branch
Logs
=================================== FAILURES ===================================
____________________________ test_presentation_mode ____________________________
Traceback (most recent call last):
File "/Users/charles/toga_dev/toga/testbed/build/testbed/macos/app/Toga Testbed.app/Contents/Resources/app_packages/_pytest/runner.py", line 341, in from_call
result: TResult | None = func()
~~~~^^
File "/Users/charles/toga_dev/toga/testbed/build/testbed/macos/app/Toga Testbed.app/Contents/Resources/app_packages/_pytest/runner.py", line 242, in <lambda>
lambda: runtest_hook(item=item, **kwds), when=when, reraise=reraise
~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^
File "/Users/charles/toga_dev/toga/testbed/build/testbed/macos/app/Toga Testbed.app/Contents/Resources/app_packages/pluggy/_hooks.py", line 513, in __call__
return self._hookexec(self.name, self._hookimpls.copy(), kwargs, firstresult)
~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/charles/toga_dev/toga/testbed/build/testbed/macos/app/Toga Testbed.app/Contents/Resources/app_packages/pluggy/_manager.py", line 120, in _hookexec
return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/charles/toga_dev/toga/testbed/build/testbed/macos/app/Toga Testbed.app/Contents/Resources/app_packages/pluggy/_callers.py", line 139, in _multicall
raise exception.with_traceback(exception.__traceback__)
File "/Users/charles/toga_dev/toga/testbed/build/testbed/macos/app/Toga Testbed.app/Contents/Resources/app_packages/pluggy/_callers.py", line 122, in _multicall
teardown.throw(exception) # type: ignore[union-attr]
~~~~~~~~~~~~~~^^^^^^^^^^^
File "/Users/charles/toga_dev/toga/testbed/build/testbed/macos/app/Toga Testbed.app/Contents/Resources/app_packages/_pytest/threadexception.py", line 92, in pytest_runtest_call
yield from thread_exception_runtest_hook()
File "/Users/charles/toga_dev/toga/testbed/build/testbed/macos/app/Toga Testbed.app/Contents/Resources/app_packages/_pytest/threadexception.py", line 68, in thread_exception_runtest_hook
yield
File "/Users/charles/toga_dev/toga/testbed/build/testbed/macos/app/Toga Testbed.app/Contents/Resources/app_packages/pluggy/_callers.py", line 122, in _multicall
teardown.throw(exception) # type: ignore[union-attr]
~~~~~~~~~~~~~~^^^^^^^^^^^
File "/Users/charles/toga_dev/toga/testbed/build/testbed/macos/app/Toga Testbed.app/Contents/Resources/app_packages/_pytest/unraisableexception.py", line 95, in pytest_runtest_call
yield from unraisable_exception_runtest_hook()
File "/Users/charles/toga_dev/toga/testbed/build/testbed/macos/app/Toga Testbed.app/Contents/Resources/app_packages/_pytest/unraisableexception.py", line 70, in unraisable_exception_runtest_hook
yield
File "/Users/charles/toga_dev/toga/testbed/build/testbed/macos/app/Toga Testbed.app/Contents/Resources/app_packages/pluggy/_callers.py", line 122, in _multicall
teardown.throw(exception) # type: ignore[union-attr]
~~~~~~~~~~~~~~^^^^^^^^^^^
File "/Users/charles/toga_dev/toga/testbed/build/testbed/macos/app/Toga Testbed.app/Contents/Resources/app_packages/_pytest/logging.py", line 846, in pytest_runtest_call
yield from self._runtest_for(item, "call")
File "/Users/charles/toga_dev/toga/testbed/build/testbed/macos/app/Toga Testbed.app/Contents/Resources/app_packages/_pytest/logging.py", line 829, in _runtest_for
yield
File "/Users/charles/toga_dev/toga/testbed/build/testbed/macos/app/Toga Testbed.app/Contents/Resources/app_packages/pluggy/_callers.py", line 122, in _multicall
teardown.throw(exception) # type: ignore[union-attr]
~~~~~~~~~~~~~~^^^^^^^^^^^
File "/Users/charles/toga_dev/toga/testbed/build/testbed/macos/app/Toga Testbed.app/Contents/Resources/app_packages/_pytest/capture.py", line 898, in pytest_runtest_call
return (yield)
^^^^^
File "/Users/charles/toga_dev/toga/testbed/build/testbed/macos/app/Toga Testbed.app/Contents/Resources/app_packages/pluggy/_callers.py", line 122, in _multicall
teardown.throw(exception) # type: ignore[union-attr]
~~~~~~~~~~~~~~^^^^^^^^^^^
File "/Users/charles/toga_dev/toga/testbed/build/testbed/macos/app/Toga Testbed.app/Contents/Resources/app_packages/_pytest/skipping.py", line 257, in pytest_runtest_call
return (yield)
^^^^^
File "/Users/charles/toga_dev/toga/testbed/build/testbed/macos/app/Toga Testbed.app/Contents/Resources/app_packages/pluggy/_callers.py", line 103, in _multicall
res = hook_impl.function(*args)
File "/Users/charles/toga_dev/toga/testbed/build/testbed/macos/app/Toga Testbed.app/Contents/Resources/app_packages/_pytest/runner.py", line 174, in pytest_runtest_call
item.runtest()
~~~~~~~~~~~~^^
File "/Users/charles/toga_dev/toga/testbed/build/testbed/macos/app/Toga Testbed.app/Contents/Resources/app_packages/pytest_asyncio/plugin.py", line 533, in runtest
super().runtest()
~~~~~~~~~~~~~~~^^
File "/Users/charles/toga_dev/toga/testbed/build/testbed/macos/app/Toga Testbed.app/Contents/Resources/app_packages/_pytest/python.py", line 1627, in runtest
self.ihook.pytest_pyfunc_call(pyfuncitem=self)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^
File "/Users/charles/toga_dev/toga/testbed/build/testbed/macos/app/Toga Testbed.app/Contents/Resources/app_packages/pluggy/_hooks.py", line 513, in __call__
return self._hookexec(self.name, self._hookimpls.copy(), kwargs, firstresult)
~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/charles/toga_dev/toga/testbed/build/testbed/macos/app/Toga Testbed.app/Contents/Resources/app_packages/pluggy/_manager.py", line 120, in _hookexec
return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/charles/toga_dev/toga/testbed/build/testbed/macos/app/Toga Testbed.app/Contents/Resources/app_packages/pluggy/_callers.py", line 182, in _multicall
return outcome.get_result()
~~~~~~~~~~~~~~~~~~^^
File "/Users/charles/toga_dev/toga/testbed/build/testbed/macos/app/Toga Testbed.app/Contents/Resources/app_packages/pluggy/_result.py", line 100, in get_result
raise exc.with_traceback(exc.__traceback__)
File "/Users/charles/toga_dev/toga/testbed/build/testbed/macos/app/Toga Testbed.app/Contents/Resources/app_packages/pluggy/_callers.py", line 103, in _multicall
res = hook_impl.function(*args)
File "/Users/charles/toga_dev/toga/testbed/build/testbed/macos/app/Toga Testbed.app/Contents/Resources/app_packages/_pytest/python.py", line 159, in pytest_pyfunc_call
result = testfunction(**testargs)
File "/Users/charles/toga_dev/toga/testbed/build/testbed/macos/app/Toga Testbed.app/Contents/Resources/app_packages/pytest_asyncio/plugin.py", line 1052, in inner
_loop.run_until_complete(task)
~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^
File "/Users/charles/toga_dev/toga/testbed/build/testbed/macos/app/Toga Testbed.app/Contents/Resources/app/tests/conftest.py", line 167, in run_until_complete
return asyncio.run_coroutine_threadsafe(coro, self.loop).result()
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^
File "/Users/charles/toga_dev/toga/testbed/build/testbed/macos/app/Toga Testbed.app/Contents/Frameworks/Python.framework/Versions/3.13/lib/python3.13/concurrent/futures/_base.py", line 456, in result
return self.__get_result()
~~~~~~~~~~~~~~~~~^^
File "/Users/charles/toga_dev/toga/testbed/build/testbed/macos/app/Toga Testbed.app/Contents/Frameworks/Python.framework/Versions/3.13/lib/python3.13/concurrent/futures/_base.py", line 401, in __get_result
raise self._exception
File "/Users/charles/toga_dev/toga/testbed/build/testbed/macos/app/Toga Testbed.app/Contents/Resources/app/tests/app/test_desktop.py", line 287, in test_presentation_mode
assert (
...<2 lines>...
), f"{window_information['window'].title}:"
AssertionError: Test Window 0:
assert <WindowState.NORMAL: 0> == <WindowState.PRESENTATION: 4>
+ where <WindowState.NORMAL: 0> = <tests_backend.window.WindowProbe object at 0x122ce7610>.instantaneous_state
+ and <WindowState.PRESENTATION: 4> = WindowState.PRESENTATION
---------------------------- Captured stdout setup -----------------------------
Resetting main_window
----------------------------- Captured stdout call -----------------------------
All Test Windows are visible
App is in presentation mode
App is in presentation mode
=========================== short test summary info ============================
FAILED tests/app/test_desktop.py::test_presentation_mode - AssertionError: Test Window 0:
assert <WindowState.NORMAL: 0> == <WindowState.PRESENTATION: 4>
+ where <WindowState.NORMAL: 0> = <tests_backend.window.WindowProbe object at 0x122ce7610>.instantaneous_state
+ and <WindowState.PRESENTATION: 4> = WindowState.PRESENTATION
============================== 1 failed in 6.54s ===============================
Additional context
No response
Thanks for the report - I can confirm I'm seeing the same failure on my (2 external + laptop) setup. It also fails with --slow enabled, which suggests it's not just a timing issue - there's something fundamental going wrong. Unfortunately, I'm not sure there's much of a fix but to add a load of print debugging to see what is and isn't happening at each step in the process.
The fact that the test isn't cleaning up is a little more concerning - I'm surprised it's possible for the test to actually complete, exit the executable, and still leave a window in presentation mode...
I'm surprised it's possible for the test to actually complete, exit the executable, and still leave a window in presentation mode...
Yeah, it's very weird, especially how it seems completely un-interactive — certainly makes it difficult to debug something when both your screens are solid black. I know you said you're seeing the same test failure — are you getting the intermittent cleanup issue too?
I know you said you're seeing the same test failure — are you getting the intermittent cleanup issue too?
Not so far - but I've only run the test a couple of times.
Gotcha. For me, it seems to happen roughly half the time.
It seems that the monitor soft-lock is indeed fixed. However, I still consistently get the same test failure locally.