diff --git a/e2e/lib/komorebi_vm/core/scenarios/handover.py b/e2e/lib/komorebi_vm/core/scenarios/handover.py index 9f1a75bb..439ee301 100644 --- a/e2e/lib/komorebi_vm/core/scenarios/handover.py +++ b/e2e/lib/komorebi_vm/core/scenarios/handover.py @@ -22,7 +22,20 @@ def run(platform: Platform, checks: Checks, clients: list[Client]) -> None: They take a window out of the daemon's hands, so a sweep of commands does not run them. The window is tiled again at the end. + + There have to be two containers or more on the focused workspace. A window that is alone there + stays on the tile it was put on, and an emptied workspace keeps its last layout (the daemon has + nothing to calculate), so "not on a tile of the layout" could never hold: the scenario says so + and stops, rather than fail on a precondition that was never met. """ + tiled = len(platform.state().workspace().containers) + if not checks.check( + "the focused workspace has two tiled containers or more, for a window to be let go of" + f" (it has {tiled})", + tiled >= 2, + ): + print(describe(platform, clients)) + return title = platform.state().focused_title() target = Client(title) before = platform.state().managed() diff --git a/komorebi-for-kde/nix/tests/vm/lib/komorebi_vm/core/scenarios/handover.py b/komorebi-for-kde/nix/tests/vm/lib/komorebi_vm/core/scenarios/handover.py index 9f1a75bb..439ee301 100644 --- a/komorebi-for-kde/nix/tests/vm/lib/komorebi_vm/core/scenarios/handover.py +++ b/komorebi-for-kde/nix/tests/vm/lib/komorebi_vm/core/scenarios/handover.py @@ -22,7 +22,20 @@ def run(platform: Platform, checks: Checks, clients: list[Client]) -> None: They take a window out of the daemon's hands, so a sweep of commands does not run them. The window is tiled again at the end. + + There have to be two containers or more on the focused workspace. A window that is alone there + stays on the tile it was put on, and an emptied workspace keeps its last layout (the daemon has + nothing to calculate), so "not on a tile of the layout" could never hold: the scenario says so + and stops, rather than fail on a precondition that was never met. """ + tiled = len(platform.state().workspace().containers) + if not checks.check( + "the focused workspace has two tiled containers or more, for a window to be let go of" + f" (it has {tiled})", + tiled >= 2, + ): + print(describe(platform, clients)) + return title = platform.state().focused_title() target = Client(title) before = platform.state().managed() diff --git a/komorebi-for-kde/nix/tests/vm/lib/komorebi_vm/platforms/kde/extras/commands.py b/komorebi-for-kde/nix/tests/vm/lib/komorebi_vm/platforms/kde/extras/commands.py index 80c86b9c..ff58f36c 100644 --- a/komorebi-for-kde/nix/tests/vm/lib/komorebi_vm/platforms/kde/extras/commands.py +++ b/komorebi-for-kde/nix/tests/vm/lib/komorebi_vm/platforms/kde/extras/commands.py @@ -6,8 +6,8 @@ windows and that every command is understood and leaves the daemon healthy, as t sees it; what is here reads KWin (`kdotool`) and the daemon's log. The suite runs `setup`, `layouts_by_count`, the shared `layouts` and `sweep`, -`replace_configuration`, the shared `handover`, `title_rule`, `prints` and `queries`, and -`at_the_end`. +`replace_configuration`, `bring_back`, the shared `handover`, `title_rule`, `prints` and +`queries`, and `at_the_end`. The daemon runs on a session with two outputs, and `clients` are five windows on the first. """ @@ -143,6 +143,42 @@ def replace_configuration(platform: KdePlatform, checks: Checks, clients: list[C ) +def bring_back(platform: KdePlatform, checks: Checks, clients: list[Client]) -> None: + """Puts the windows the sweep sent to other workspaces back on the first, a container each, and + focuses it, so that the scenarios after it start from the windows the sweep left, on one + workspace, as the shared `handover` needs (a window alone on its workspace has no layout to be + left out of). + """ + + def count() -> int: + return len(platform.state().workspace(0).windows()) + + for _ in clients: + elsewhere = [ + i + for i, ws in enumerate(platform.state().monitors[0].workspaces) + if i != 0 and ws.windows() + ] + if not elsewhere: + break + here = count() + platform.komorebic("focus-workspace", str(elsewhere[0])) + platform.komorebic("send-to-workspace", "0") + platform.wait_for(lambda h=here: count() > h, timeout=10) + platform.komorebic("focus-workspace", "0") + state = platform.state + checks.check( + "the windows are back on the first workspace, in view, in a container each", + platform.wait_for( + lambda: ( + len(state().workspace(0).containers) >= 2 + and state().monitors[0].focused_workspace == 0 + ), + timeout=10, + ), + ) + + def at_the_end(platform: KdePlatform, checks: Checks, clients: list[Client]) -> None: """The daemon has every window KWin has, each is where the daemon last asked for it, and a clean stop leaves the daemon not running.""" diff --git a/komorebi-for-kde/nix/tests/vm/lib/tests/test_moved.py b/komorebi-for-kde/nix/tests/vm/lib/tests/test_moved.py index 675bd9d6..8b484c55 100644 --- a/komorebi-for-kde/nix/tests/vm/lib/tests/test_moved.py +++ b/komorebi-for-kde/nix/tests/vm/lib/tests/test_moved.py @@ -60,7 +60,7 @@ def test_a_daemon_that_lets_go_and_takes_up_again_passes_handover() -> None: base = checks.passed handover.run(platform, checks, clients) assert checks.failed == 0, checks.failures - assert checks.passed - base == 5 + assert checks.passed - base == 6 def test_a_daemon_that_keeps_an_unmanaged_window_fails_handover() -> None: @@ -332,3 +332,14 @@ def test_a_crash_that_leaves_a_window_hidden_fails() -> None: clients = opened(platform, checks) lifecycle_hidden.crash(platform, checks, clients) assert any("none is left out of sight" in f for f in checks.failures), checks.failures + + +def test_a_window_that_is_alone_on_its_workspace_fails_handover_on_the_precondition() -> None: + platform = FakePlatform() + checks = Checks() + clients = opened(platform, checks) + platform.komorebic("send-to-workspace", "1") + platform.komorebic("send-to-workspace", "1") + handover.run(platform, checks, clients) + assert any("two tiled containers" in f for f in checks.failures) + assert not any("unmanage" in f for f in checks.failures) diff --git a/komorebi-for-kde/nix/tests/vm/suites/commands/test.py b/komorebi-for-kde/nix/tests/vm/suites/commands/test.py index 9b6bb901..9a019831 100644 --- a/komorebi-for-kde/nix/tests/vm/suites/commands/test.py +++ b/komorebi-for-kde/nix/tests/vm/suites/commands/test.py @@ -45,6 +45,7 @@ run_suite( platform_only("kde", kde.komorebic, "change-layout", "bsp"), shared(commands.sweep, *args, CONFIG), platform_only("kde", kde_commands.replace_configuration, *args), + platform_only("kde", kde_commands.bring_back, *args), shared(handover.run, *args), shared(title_rule.run, platform, checks), shared(commands.prints, platform, checks),