mirror of
https://github.com/LGUG2Z/komorebi.git
synced 2026-11-14 12:53:43 +01:00
feat(nix): the manage_rules and state_fidelity scenarios
This commit is contained in:
1 parent
ac3a9853a7
commit
6748528b90
5 files changed
+184
-5
No files matched your search
@@ -0,0 +1,73 @@
|
||||
"""A manage rule has a window managed that an ignore rule leaves alone, by title, class and exe."""
|
||||
|
||||
from typing import Any
|
||||
|
||||
from komorebi_vm.core.checks import Checks
|
||||
from komorebi_vm.core.platform import Client, Platform
|
||||
|
||||
# how long a window has to stay unmanaged for it to be left alone
|
||||
QUIET = 3
|
||||
|
||||
|
||||
def _kinds(platform: Platform) -> list[tuple[str, str, dict[str, Any]]]:
|
||||
"""For each kind of rule: its name, the id of the rule, and what the window is opened with."""
|
||||
return [
|
||||
("title", "e2e-manage-title", {}),
|
||||
("class", "e2e-manage-class", {"app_id": "e2e-manage-class"}),
|
||||
("exe", platform.exe_id("e2e-manage-exe"), {"exe": "e2e-manage-exe"}),
|
||||
]
|
||||
|
||||
|
||||
def run(platform: Platform, checks: Checks) -> None:
|
||||
"""For each kind of rule, an ignore rule leaves a window alone and a manage rule for the same
|
||||
application then has the next one managed and tiled.
|
||||
|
||||
A window the daemon leaves alone does not make room for itself: the window that is tiled keeps
|
||||
its frame. The daemon must be running, with no window, and the rules are for names no other
|
||||
scenario uses, because `ignore-rule` drops a rule whose id an earlier rule has.
|
||||
"""
|
||||
base = platform.spawn("manage-base")
|
||||
platform.wait_for(lambda: platform.state().workspace_of(base.title) == 0, timeout=20)
|
||||
for kind, rule_id, how in _kinds(platform):
|
||||
title = rule_id if kind == "title" else f"manage-{kind}"
|
||||
platform.komorebic("ignore-rule", kind, rule_id)
|
||||
before = platform.os_window(base)
|
||||
ignored = platform.spawn(f"{title} ignored", **how)
|
||||
checks.check(
|
||||
f"a window an ignore rule by {kind} matches is not managed",
|
||||
not platform.wait_for(
|
||||
lambda c=ignored: platform.state().workspace_of(c.title) is not None,
|
||||
timeout=QUIET,
|
||||
),
|
||||
)
|
||||
after = platform.os_window(base)
|
||||
checks.check(
|
||||
f"... and the window that is tiled keeps its place ({kind})",
|
||||
before is not None and after is not None and before.frame == after.frame,
|
||||
)
|
||||
platform.close(ignored)
|
||||
platform.wait_for(lambda c=ignored: platform.os_window(c) is None, timeout=20)
|
||||
|
||||
platform.komorebic("manage-rule", kind, rule_id)
|
||||
managed: Client = platform.spawn(f"{title} managed", **how)
|
||||
checks.check(
|
||||
f"with a manage rule by {kind} too, a window it matches is managed",
|
||||
platform.wait_for(
|
||||
lambda c=managed: platform.state().workspace_of(c.title) == 0, timeout=20
|
||||
),
|
||||
)
|
||||
checks.check(
|
||||
f"... and tiled: {base.title} gives it room ({kind})",
|
||||
platform.wait_for(
|
||||
lambda b=before: (
|
||||
(a := platform.os_window(base)) is not None
|
||||
and b is not None
|
||||
and a.frame != b.frame
|
||||
),
|
||||
timeout=20,
|
||||
),
|
||||
)
|
||||
platform.close(managed)
|
||||
platform.wait_for(lambda c=managed: platform.state().workspace_of(c.title) is None)
|
||||
platform.close(base)
|
||||
platform.wait_for(lambda: platform.state().managed() == 0, timeout=20)
|
||||
@@ -0,0 +1,48 @@
|
||||
"""The daemon's state names each window as it was opened: its title, its program and its class."""
|
||||
|
||||
from komorebi_vm.core.checks import Checks
|
||||
from komorebi_vm.core.platform import Platform
|
||||
|
||||
|
||||
def _window(platform: Platform, title: str): # noqa: ANN202
|
||||
for workspace in platform.state().workspace().windows():
|
||||
if workspace.title == title:
|
||||
return workspace
|
||||
return None
|
||||
|
||||
|
||||
def run(platform: Platform, checks: Checks) -> None:
|
||||
"""Opens a window with only a title, one with a class, and one made by a program of its own,
|
||||
and checks what the state says of each: the title as given, the class that was set, and the
|
||||
program (through `exe_id`, which is its file name on the platform).
|
||||
|
||||
The rules scenarios check that a rule finds a window by these; this checks that what the state
|
||||
shows of a window is what the window was given. The daemon must be running, with no window.
|
||||
"""
|
||||
plain = platform.spawn("fidelity plain")
|
||||
classed = platform.spawn("fidelity class", app_id="e2e-fidelity-class")
|
||||
made = platform.spawn("fidelity exe", exe="e2e-fidelity-exe")
|
||||
platform.wait_for(lambda: platform.state().managed() == 3, timeout=30)
|
||||
|
||||
found = _window(platform, plain.title)
|
||||
checks.check("the state has the title of a window as it was opened", found is not None)
|
||||
checks.check(
|
||||
"... and says what program opened it",
|
||||
found is not None and found.exe != "",
|
||||
)
|
||||
found = _window(platform, classed.title)
|
||||
checks.check(
|
||||
"the state has the class a window was given",
|
||||
found is not None and found.window_class == "e2e-fidelity-class",
|
||||
)
|
||||
found = _window(platform, made.title)
|
||||
checks.check(
|
||||
"the state has the program that opened a window, as its file name",
|
||||
found is not None and found.exe == platform.exe_id("e2e-fidelity-exe"),
|
||||
)
|
||||
for client in (plain, classed, made):
|
||||
platform.close(client)
|
||||
checks.check(
|
||||
"the three windows are closed",
|
||||
platform.wait_for(lambda: platform.state().managed() == 0, timeout=20),
|
||||
)
|
||||
@@ -93,9 +93,12 @@ class KdePlatform:
|
||||
program = "foot"
|
||||
if exe:
|
||||
# the daemon reads the executable from /proc, where a link shows its target: a copy
|
||||
# of the terminal is what has the name
|
||||
# of the terminal is what has the name, made once: a copy that is running cannot be
|
||||
# written over
|
||||
program = f"{BIN}/{exe}"
|
||||
self.kde.sh(f"mkdir -p {BIN} && cp -L $(command -v foot) {program}")
|
||||
self.kde.sh(
|
||||
f"mkdir -p {BIN} && {{ test -x {program} || cp -L $(command -v foot) {program}; }}"
|
||||
)
|
||||
self.kde.spawn(f"{program} {name}-T {shlex.quote(title)} sleep 3000")
|
||||
self.kde.wait_for(lambda: self.kde.wid(title) != "", timeout=30)
|
||||
return Client(title)
|
||||
|
||||
@@ -87,6 +87,8 @@ class FakePlatform:
|
||||
self.exe_rules: dict[str, int] = {}
|
||||
self.ignored_exes: set[str] = set()
|
||||
self.exes: dict[str, str] = {}
|
||||
self.classes: dict[str, str] = {}
|
||||
self.manage: set[tuple[str, str]] = set()
|
||||
self.names: list[str | None] = [None, None, None]
|
||||
self.layout_name = "Columns"
|
||||
self.reloads = reloads
|
||||
@@ -171,7 +173,17 @@ class FakePlatform:
|
||||
return [stack[0] for stack in self.containers]
|
||||
|
||||
def spawn(self, title: str, app_id: str | None = None, exe: str | None = None) -> Client:
|
||||
self.exes[title] = exe or ""
|
||||
self.exes[title] = exe or "foot"
|
||||
self.classes[title] = app_id or ""
|
||||
forced = (
|
||||
("exe", exe) in self.manage
|
||||
or ("class", app_id) in self.manage
|
||||
or any(kind == "title" and title.startswith(i) for kind, i in self.manage)
|
||||
)
|
||||
if forced:
|
||||
self.containers.append([title])
|
||||
self._relayout()
|
||||
return Client(title)
|
||||
if exe in self.ignored_exes:
|
||||
self.unmanaged.append(title)
|
||||
return Client(title)
|
||||
@@ -187,7 +199,7 @@ class FakePlatform:
|
||||
if self.paused:
|
||||
self.unmanaged.append(title)
|
||||
return Client(title)
|
||||
if title in self.ignored:
|
||||
if any(title.startswith(i) for i in self.ignored):
|
||||
self.unmanaged.append(title)
|
||||
elif title in self.initial_rules:
|
||||
self.spaces[self.initial_rules.pop(title)].append([title])
|
||||
@@ -451,6 +463,8 @@ class FakePlatform:
|
||||
self.ignored_classes.add(name)
|
||||
case ("ignore-rule", "title", title):
|
||||
self.ignored.add(title)
|
||||
case ("manage-rule", kind, name):
|
||||
self.manage.add((kind, name))
|
||||
case ("move-to-workspace", number):
|
||||
self.komorebic("send-to-workspace", number)
|
||||
self.last, self.current = self.current, int(number)
|
||||
@@ -528,7 +542,7 @@ class FakePlatform:
|
||||
"details": {
|
||||
"title": title,
|
||||
"exe": self.exes.get(title, ""),
|
||||
"class": "",
|
||||
"class": self.classes.get(title, ""),
|
||||
"path": "",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ from komorebi_vm.core.scenarios import (
|
||||
lifecycle,
|
||||
lifecycle_hidden,
|
||||
locking,
|
||||
manage_rules,
|
||||
maximizing,
|
||||
minimizing,
|
||||
monocle,
|
||||
@@ -32,6 +33,7 @@ from komorebi_vm.core.scenarios import (
|
||||
scrolling,
|
||||
stacks,
|
||||
startup_rules,
|
||||
state_fidelity,
|
||||
tiling,
|
||||
tiling_off,
|
||||
workspace_moves,
|
||||
@@ -507,6 +509,45 @@ def test_a_daemon_that_does_not_tile_again_on_unlock_fails_locking() -> None:
|
||||
assert checks.failed > 0
|
||||
|
||||
|
||||
def test_a_daemon_that_is_right_passes_manage_rules() -> None:
|
||||
platform = FakePlatform()
|
||||
checks = Checks()
|
||||
manage_rules.run(platform, checks)
|
||||
assert checks.failed == 0, checks.failures
|
||||
assert checks.passed > 8
|
||||
|
||||
|
||||
def test_a_daemon_that_ignores_manage_rules_fails_manage_rules() -> None:
|
||||
class Deaf(FakePlatform):
|
||||
def komorebic(self, *args: str) -> str:
|
||||
return "" if args[0] == "manage-rule" else super().komorebic(*args)
|
||||
|
||||
platform = Deaf()
|
||||
checks = Checks()
|
||||
manage_rules.run(platform, checks)
|
||||
assert checks.failed > 0
|
||||
|
||||
|
||||
def test_a_daemon_that_shows_what_a_window_was_given_passes_state_fidelity() -> None:
|
||||
platform = FakePlatform()
|
||||
checks = Checks()
|
||||
state_fidelity.run(platform, checks)
|
||||
assert checks.failed == 0, checks.failures
|
||||
assert checks.passed > 4
|
||||
|
||||
|
||||
def test_a_daemon_that_loses_the_class_fails_state_fidelity() -> None:
|
||||
class Forgetful(FakePlatform):
|
||||
def spawn(self, title, app_id=None, exe=None): # noqa: ANN001, ANN202
|
||||
client = super().spawn(title, app_id=None, exe=exe)
|
||||
return client
|
||||
|
||||
platform = Forgetful()
|
||||
checks = Checks()
|
||||
state_fidelity.run(platform, checks)
|
||||
assert checks.failed > 0
|
||||
|
||||
|
||||
def test_a_command_the_port_lacks_is_skipped_by_name() -> None:
|
||||
platform = FakePlatform()
|
||||
checks = Checks()
|
||||
|
||||
Reference in new issue
Block a user