Gate candidate 3c: wip-cand3b (cand3 + the bar e2e scenario) plus wip-u-kdefix (handover precondition in both copies of the shared e2e lib, KDE vm-commands bring_back step). The shared e2e/lib handover scenario is also what the Windows runner runs, so the Windows gate must include it.
Scenario precondition fix; the check "the unmanaged window is inside the work area and not on a
tile of the layout" itself stays as strict as it was.
The KDE VM suite vm-commands failed it on a calm host, on the original komorebi-for-kde tip
(7d19fd40) and on the refactored stage alike, with the same numbers each time: work area
(0,0,1920,1080), layout a single tile (10,10,1900,1060), win3 at (10,10,1896,1050) (within the
24 px tolerance of that tile), win1/win2/win5 parked on workspace 3. After the sweep win3 was the
only window on its workspace. Unmanaging a lone window leaves it on the tile it was put on, and
an emptied workspace has nothing to calculate, so it keeps its last layout; "not on a tile" could
never hold. The scenario now checks first that the focused workspace has at least two tiled
containers and, if not, fails with a message that says so (and the describe() dump) instead of
the misleading unmanage failure. Windows runs handover as its own suite with its three tiled
clients on workspace 0, so the precondition holds there.
The same file lives in e2e/lib and in komorebi-for-kde/nix/tests/vm/lib; both are edited alike. A
fake-platform test covers the precondition; the existing handover cases still fail where they
should.
Native pytest of the VM lib: 320 passed; ruff check and format clean.
VM verification: not yet (vm-commands on baseline-with-fix and candidate, ~20 min each, after the
KDE VM run).
New extras scenario e2e/platforms/qemu_windows/extras/bar.py, registered in
run.py as `bar` (runs after the tiling windows, like the other Windows
extras). It starts komorebi-bar in the guest with a configuration that has
storage, time, applications, komorebi workspaces and layout widgets, the
Windows-only Keyboard and Systray widgets and a `mouse` block (a message and
a command), and checks: `komorebi-bar --schema` equals the checked-in
schema.bar.json byte for byte; the process starts and its window titled
komorebi-bar appears, visible and sized; it runs 15 s (scaled); `komorebic
state` answers and komorebi does not tile the bar; commands that notify the
bar work; the bar survives `komorebic stop` and the daemon starting again as
the same process (it reconnects: it does NOT exit when the daemon stops, so
the task text "exits cleanly when komorebi stops" is checked as
`komorebic stop --bar` ending daemon and bar); screenshots are kept as
artefacts, no pixel assertions. No known failures.
Plumbing: run.py uploads komorebi-bar.exe when the binaries directory has it
(other scenarios do not need it, so older build-ref directories still work),
the nix app copies it and sets KOMOREBI_E2E_BAR_SCHEMA to the checked-in
schema (the e2e directory alone does not contain it), build-ref.sh builds it.
README and docs/dev/bar-e2e.md document it.
Verified: py_compile and ruff clean; the runner and library unit tests: 157
passed; flake.nix parses. NOT run: the scenario itself (the Windows guest is
reserved for gates), so every check is untested against a real bar and may
need adjusting (window title, timings, the Systray widget inside the VM).
Needs gate: win-e2e `bar`.
e2e/guest/testwin/src/main.rs had an unsafe block inside an unsafe block, which clippy -D warnings rejects (unused_unsafe). Removes the inner one without changing behaviour. Verified: Windows clippy passes.
The tests are cross-compiled for Windows, so they run there: cargo_tests.py
uploads each test executable, lists its tests and runs every one in a
process of its own, as nextest does. The nix check never ran them
(doCheck is false).
On a komorebi with 3fa3d429 the step passes its tracking checks; what is
left is that toggle-float and stack left/right are not ignored while a
window is maximized. The marker says so for such a build.
komorebi-testwin-e2e is the e2e build's test window, so a change to it
rebuilds the crate and not its dependencies (86 s instead of minutes);
build-ref.sh uses it.
The pixel checks of the shared scenarios (the ring of a border by hue) take
the window's own color to be neutral. The default was (48, 80, 128), which
is cool by their measure, so a warm ring beside it read as mixed.
maximizing.user is a known difference only on a komorebi without 3fa3d429
(track a window the user maximizes or restores); on a build that has it
the step runs as any other.
put_file writes bytes to a path in the guest (a folder that has a space in
its name is made), wallpaper and set_wallpaper read and set the wallpaper
of a display through IDesktopWallpaper, with the COM calls in C# compiled by
Add-Type (wallpaper.ps1).
The left, right and bottom borders of a window reach outside its visible
frame (the invisible DWM border), so the press is 4 px outside; the top has
none and its border is inside the window, so the press is in its first
rows. Outside the top the press hit the desktop and no drag started.
maximizing.user (a user's maximize is not the maximized window) and
hotplug.user_move (only a drag relocates a window to another monitor).
Their failing checks are known failures with the reason; a step that has
no failing check says the marker can go.
crash() killed the daemon but not the command that launched it, which wrote
its exit code into the output file after the next launch had started, so
that launch looked as if it had ended and was started again.
QEMU has died with SIGABRT while a second display was plugged in (a
hotplug repeat and monitors.at_cursor), which is not a result about kfw.
Its stderr is kept, a dead guest is named as such, and the scenario is
run once more on a fresh guest.
--rename-to T --after S gives the window another title S seconds after it
opens, as a program that sets its title late does. The message WM_APP + 2
takes it fullscreen, borderless over its monitor, and back.
monitors, layout_flip, title_rule, handover, queries, display_change and
fullscreen, with the extra functions their files have after run
(hotplug.user_move, resize.edge_cases, maximizing.user,
lifecycle_hidden.crash, monocle.focus_behaviour, monitors.at_cursor).
spawn takes rename_to and after (the test window renames itself),
temp_path, crash (kills the daemon), fullscreen and user_maximize (the
user's own, by messages the window understands), with tests: a window
that is not there raises, a program name Windows rejects is rejected.
Any edit to guest.py made a new state (76 to 145 s). The state now follows
the command line of the guest that is saved, with the paths as
placeholders, so an edit to the rest of guest.py does not.
komorebic state can print something else while the daemon is busy or in
the middle of a monitor change. The runner crashed on it (a hotplug run
did); now it is a RuntimeError with what was printed, which waits retry.
The runner reads the commit from the binaries' commit file and asks git
whether it contains the fix of the bug a marker is for, so the same runner
tests a branch with the bug and one without it.
With two windows on the second display, its mode is changed to two others
and back: the daemon's monitor follows, the windows stay inside the work
area and apart, and return to where they were. A draft for a shared
scenario.
display.ps1 lists the display devices and changes the mode of one (the
logic is in C# because PowerShell cannot pass the structs by reference),
and WindowsPlatform.resize_display uses it. Only the QXL display lists
modes; the first one is the basic adapter, which has one.
Every scenario goes through run_suite, so its checks are labelled
[shared] and the log says which scenario a check came from. Keyword
arguments go through a small wrapper because a step takes positional
ones.
padding (the border's 7 px), cloaking (and what stop does to it), the
config file watch, the system's window tracking behind focus follows
mouse, and a second display with a window sent to it. Each is run after
the tiling windows as windows_<name>, its checks labelled [windows].
The daemon watches the configuration file it was started with, so the
extra replace-configuration after each write hid the reload that the
scenario is meant to check.