docs(e2e): how the macOS guest works and how to run it

A README for e2e/platforms/tart_macos: what a run does, why the grants live in the golden image and
are inherited through Terminal, why the agent is a Rust program (the stock image has no developer
tools), and the limits (one display, no input, two guests per host, Apple hardware only, so not in
CI). docs/dev/ci.md and e2e-to-unit.md said macOS had no integration coverage; they now say where it
runs and what it cannot reach.
This commit is contained in:
LGUG2Z committed 2026-11-06 14:40:50 -08:00
1 parent 98b3b4bc0c
commit 18d7f9a506
3 files changed
+83 -5

No files matched your search

+5 -3
View File
@@ -70,15 +70,17 @@ installs nextest on the Windows runners, which have no nix, and `windows.yaml` (
| format | `fmt`, `checkSets.fmt` | the same | the same |
| clippy | `lint` (cross build) | `lint` | `macos-hosted` only (`macos-lint`); `mac-typecheck` type-checks on Linux |
| unit tests | `windows-native-test` on Windows, and `windows.yaml` on every event | `test` (`linux-test`) | `macos-hosted` only |
| integration | none (the guest is experimental: `windows-guest.yaml`) | `vm-fast`, `vm-slow` | none |
| integration | none (the guest is experimental: `windows-guest.yaml`) | `vm-fast`, `vm-slow` | none in CI; by hand on a Mac: `nix run .#e2e-macos` (a Tart guest, `e2e/platforms/tart_macos`) |
| options and schemas | `linux-options`, `macos-options` on Linux; the Windows schemas are checked by the Windows e2e Python | `linux-options` | `macos-options` on Linux; the schemas by `macos-hosted` |
The gaps:
- No Windows e2e: the guest needs the evaluation ISO and about 20 minutes of Windows Setup, and nothing says a hosted runner can do it.
- macOS runs on every change to its paths, on `macos-latest` (Apple silicon, the latest macOS, the only one supported): standard hosted runners are free and
unlimited on a public repository. Nothing starts the daemon there, so tiling on macOS is not exercised; whether a hosted runner has a GUI session with the
Accessibility and Screen Recording permissions is not known.
unlimited on a public repository. Nothing starts the daemon there, so tiling on macOS is not exercised in CI; whether a hosted runner has a GUI session with the
Accessibility and Screen Recording permissions is not known. The shared e2e scenarios do run on macOS, by hand, on a Mac (`nix run .#e2e-macos`,
`e2e/platforms/tart_macos/README.md`): a hosted runner is itself a virtual machine and cannot start a macOS guest, and a guest runs on Apple
hardware only, two at a time, so CI would need a Mac of its own.
- The nightly rustfmt of `fmt` is the nightly of the day; `checkSets.fmt` in stage 1 uses the one the flake pins. When only `fmt` fails, the nightly moved.
- `mac-typecheck` takes the std of `aarch64-apple-darwin` from the toolchain of the runner (`rustup target add`), because the dev shell of the
flake only has the Windows target. clippy could run the same way (`cargo clippy --target aarch64-apple-darwin`); `tools/mac-check.sh` does not.
+4 -2
View File
@@ -59,9 +59,11 @@ times (Windows `komorebi/src/window.rs:1178`, KDE `window.rs:661`, macOS `window
| lifecycle (7), lifecycle_hidden (13), locking (7), handover (6), fullscreen (6), display_change (4), focus_follows_mouse (7) | stop/start restores windows, screen lock, minimize/unmanage/manage, fullscreen, mode change, pointer focus | A for hide/restore: `container_behaviour.rs` `restoring_*`, `workspace_layers.rs` `hiding_a_workspace_*`; the process, session, OS events and pointer are C | - | - | - |
| Windows extras: bar (21), windows_padding, cloaking, config_file, tracking, second_display, display_change | bar process and window, 7 px border, cloaking, file watch, system focus setting, QXL display | bar schema: A (`crates/komorebi-bar-core/tests/schema_oracle.rs`); rest C | - | - | - |
## macOS-origin behaviour (never e2e-tested): what the shared tests now give
## macOS-origin behaviour: what the shared tests give besides the e2e
macOS has no e2e. The shared-crate tests that exist protect its code path as far as the hooks do:
The shared e2e scenarios run on macOS in a Tart guest, by hand on a Mac (`nix run .#e2e-macos`,
`e2e/platforms/tart_macos/README.md`; not in CI). What a macOS guest cannot do (a second display, a
pointer, a lock screen) is still never e2e-tested. The shared-crate tests that exist protect its code path as far as the hooks do:
`state_roundtrip.rs` `a_macos_shaped_state_is_written_as_it_was_read_with_the_corner_last`,
`a_macos_state_cannot_be_read_without_its_corner`, the `StubWindow` that is `Clone` but not `Copy`,
and now `e2e_layouts.rs` (the tiles every macOS tiling goes through). Proposed B tests, platform
+74
View File
@@ -0,0 +1,74 @@
# The macOS guest
The scenarios of the shared core (`e2e/lib/komorebi_vm/core/scenarios`) run against the real
`komorebi` daemon of macOS in a virtual machine, the way they run on KDE (a NixOS test) and on Windows
(`../qemu_windows`). A scenario sees a [`Platform`](../../lib/komorebi_vm/core/platform.py); this
directory is the one for macOS.
```text
host (Apple silicon) guest (a clone of the golden image)
python -m platforms.tart_macos.run macOS, logged in, one 1280x800 display
Guest ── tart clone / run --no-graphics ──► Terminal (holds the grants)
MacosPlatform ── agent protocol, TCP 7878 ──► komorebi-e2e-agent answers the host
komorebi the daemon under test
komorebi-testwin the windows to tile
```
## What a run does
For each scenario, `Guest` clones the golden image (copy on write, so it costs nothing), boots it
without a window, and waits for the agent, which answers about 20 s after the clone starts. The programs
under test are uploaded through the agent, the daemon is started by it, the shared scenario runs, and
the clone is deleted. `OUT/<scenario>/result.json` names the scenario, the commit and every check, as
the Windows runner's does; a failed run also keeps the daemon's output, the processes and a screenshot.
## Why it is built this way
- **Permissions are the hard part.** The daemon needs Screen Recording and Accessibility, which macOS
only grants to a person's click. They are granted once to `Terminal` in the golden image and are
inherited by every clone. The agent is started at login by a launch agent that opens Terminal on it
(`provision.sh`), so the agent, the daemon it spawns and the test windows are all attributed to
Terminal, and no clone asks anything. A signed binary of our own would need a grant per build.
- **The agent is a Rust program** (`crates/komorebi-e2e-agent`, the same crate and protocol as the
Windows agent, with `macos.rs` for the verbs) because the stock image has no developer tools: `swift`,
`clang` and `python3` there are stubs. It reads windows and displays through the accessibility API, as
the daemon does, so a window's frame is what the application says.
- **No SSH at run time.** The vanilla image has `admin`/`admin` and SSH on, which only
`build-golden.sh` uses.
- **The windows that are not the test's are left alone** by an ignore rule the platform adds to every
configuration: the Terminal, and the desktop widgets of the image (Notification Center draws them as
windows, and they are tiled otherwise).
## Limits
- **One display.** A macOS guest has one virtual display, so what needs a second one (`hotplug`,
`monitors`, `display_change`, `unknown_monitor`) is skipped with the reason.
- **No input is injected**, so there is no pointer (`focus_follows_mouse`, drags) and no lock screen.
- **At most two guests at once on a host.** Apple's licence and Virtualization.framework both stop a
third, so a host runs two scenarios in parallel at the most.
- **Apple hardware only.** A hosted GitHub runner is itself a virtual machine and cannot start one, so
this does not run in CI: it runs on a Mac you have (see `docs/dev/ci.md`).
- **The wallpaper and the zoom button** are not driven yet.
## Running it
```sh
# once: the golden image (30 GB of disk; one manual step, the grants, which the script stops for)
e2e/platforms/tart_macos/build-bins.sh target/e2e-macos
e2e/platforms/tart_macos/build-golden.sh target/e2e-macos
# every time: this checkout's daemon against the image
nix run .#e2e-macos -- target/e2e-out tiling focus
```
`nix run .#e2e-macos` builds the programs under test and brings `tart` (nixpkgs has it as an unfree
package, so it is allowed for that one only). `KOMOREBI_E2E_BINS=dir` runs the programs of `dir`
(`build-bins.sh` makes one), `KOMOREBI_E2E_GOLDEN=name` another image, `KOMOREBI_E2E_SCALE=1.5` makes
every wait longer for a busy host. The image has to be built again when the agent changes, and for a
new macOS.
The unit tests of the port need no guest and run with those of the Windows one
(`python -m pytest lib/tests platforms/qemu_windows/tests platforms/tart_macos/tests`, in `e2e`, with
`PYTHONPATH=lib:.`; the `windows-e2e-tests` check does it).
Do not run it alongside a heavy build, which makes the daemon slow to answer and the waits fail.