test+fix: close #14, V2 #57 live install entry, #41 float slice
All checks were successful
Check / eval (push) Successful in 2m57s
All checks were successful
Check / eval (push) Successful in 2m57s
- #14: first scheduled lock bump confirmed (8fded63 nomarchy-bump Mon
05:20 UTC 2026-07-06); remove queue item.
- #57 V2: checks.live-install-entry builds nomarchy-live HM gen and
asserts Install Nomarchy.desktop + installer bin + menu gate.
- #41: float/center hyprpolkitagent + pinentry-qt from package app-ids;
capture-float-classes.nix harness (softGL empty); residual portal
class stays NEXT [blocked:hw]. HARDWARE-QUEUE confirm steps.
Verified: live-install-entry + windowrule-syntax green.
This commit is contained in:
124
tools/capture-float-classes.nix
Normal file
124
tools/capture-float-classes.nix
Normal file
@@ -0,0 +1,124 @@
|
||||
# Capture window classes for BACKLOG #41 float candidates (hyprpolkitagent,
|
||||
# pinentry-qt, optional portal). Headless softGL Hyprland; dumps hyprctl
|
||||
# clients while dialogs are open into $out/classes.json + classes.txt.
|
||||
#
|
||||
# nix build --impure -f tools/capture-float-classes.nix --no-link --print-out-paths
|
||||
#
|
||||
# Maintainer harness — not a checks.* gate (needs interactive dialogs;
|
||||
# softGL may drop some Qt windows).
|
||||
let
|
||||
flake = builtins.getFlake ("git+file://" + toString ../.);
|
||||
inherit (flake.inputs) nixpkgs home-manager;
|
||||
pkgs = import nixpkgs {
|
||||
system = "x86_64-linux";
|
||||
overlays = [ flake.overlays.default ];
|
||||
config.allowUnfree = true;
|
||||
};
|
||||
in
|
||||
pkgs.testers.runNixOSTest {
|
||||
name = "capture-float-classes";
|
||||
node.pkgsReadOnly = false;
|
||||
node.specialArgs = { username = "nomarchy"; };
|
||||
nodes.machine = { pkgs, lib, ... }: {
|
||||
imports = [ flake.nixosModules.nomarchy home-manager.nixosModules.home-manager ];
|
||||
virtualisation.memorySize = 4096;
|
||||
virtualisation.cores = 2;
|
||||
hardware.graphics.enable = true;
|
||||
environment.variables.LIBGL_ALWAYS_SOFTWARE = "1";
|
||||
boot.initrd.availableKernelModules = [ "virtio_gpu" ];
|
||||
virtualisation.qemu.options = [ "-vga none" "-device virtio-gpu-pci,xres=1280,yres=800" ];
|
||||
|
||||
users.users.nomarchy = {
|
||||
isNormalUser = true;
|
||||
password = "test";
|
||||
extraGroups = [ "wheel" "video" ];
|
||||
};
|
||||
security.sudo.wheelNeedsPassword = false;
|
||||
# PolicyKit test action so pkexec can raise the agent.
|
||||
security.polkit.enable = true;
|
||||
|
||||
nomarchy.system.stateFile = flake + "/theme-state.json";
|
||||
systemd.tmpfiles.rules = [
|
||||
"d /home/nomarchy/.nomarchy 0755 nomarchy users -"
|
||||
"C /home/nomarchy/.nomarchy/theme-state.json 0644 nomarchy users - ${flake + "/theme-state.json"}"
|
||||
];
|
||||
services.greetd.settings.initial_session = {
|
||||
command = "start-hyprland";
|
||||
user = "nomarchy";
|
||||
};
|
||||
home-manager.useGlobalPkgs = true;
|
||||
home-manager.users.nomarchy = {
|
||||
imports = [ flake.homeModules.nomarchy ];
|
||||
nomarchy.stateFile = flake + "/theme-state.json";
|
||||
nomarchy.idle.enable = false;
|
||||
# Ensure pinentry-qt is the active pinentry (keys.nix default).
|
||||
};
|
||||
};
|
||||
testScript = ''
|
||||
machine.wait_for_unit("multi-user.target")
|
||||
machine.wait_for_file("/run/user/1000/hypr", 180)
|
||||
machine.sleep(15)
|
||||
|
||||
hy_env = "XDG_RUNTIME_DIR=/run/user/1000 HYPRLAND_INSTANCE_SIGNATURE=$(ls /run/user/1000/hypr | head -1)"
|
||||
hyctl = f"su - nomarchy -c '{hy_env} hyprctl"
|
||||
|
||||
def clients():
|
||||
return machine.succeed(f"{hyctl} clients -j'").strip()
|
||||
|
||||
dumps = []
|
||||
|
||||
# ── pinentry-qt (keys.nix default) ───────────────────────────────
|
||||
machine.execute(
|
||||
f"su - nomarchy -c '{hy_env} timeout 8 pinentry-qt' >/tmp/pinentry.log 2>&1 &"
|
||||
)
|
||||
machine.sleep(3)
|
||||
dumps.append(("pinentry", clients()))
|
||||
machine.execute("pkill -x pinentry-qt >/dev/null 2>&1 || true")
|
||||
machine.sleep(1)
|
||||
|
||||
# ── hyprpolkitagent via pkexec (passwordless sudo wheel — may still
|
||||
# open agent depending on polkit rules; best-effort) ────────────
|
||||
machine.execute(
|
||||
f"su - nomarchy -c '{hy_env} timeout 8 pkexec true' >/tmp/pkexec.log 2>&1 &"
|
||||
)
|
||||
machine.sleep(3)
|
||||
dumps.append(("pkexec", clients()))
|
||||
machine.execute("pkill -x hyprpolkitagent >/dev/null 2>&1 || true")
|
||||
machine.execute("pkill -f pkexec >/dev/null 2>&1 || true")
|
||||
machine.sleep(1)
|
||||
|
||||
# ── Always dump current clients for residual ─────────────────────
|
||||
dumps.append(("idle", clients()))
|
||||
|
||||
import json
|
||||
out_path = machine.succeed("mktemp -d").strip()
|
||||
summary = []
|
||||
for label, raw in dumps:
|
||||
path = f"{out_path}/{label}.json"
|
||||
machine.succeed(f"cat > {path} <<'EOF'\n{raw}\nEOF")
|
||||
try:
|
||||
data = json.loads(raw) if raw else []
|
||||
except Exception as e:
|
||||
data = []
|
||||
summary.append(f"{label}: parse error {e}")
|
||||
classes = sorted({
|
||||
c.get("class") or c.get("initialClass") or ""
|
||||
for c in (data if isinstance(data, list) else [])
|
||||
} - {""})
|
||||
titles = sorted({
|
||||
c.get("title") or c.get("initialTitle") or ""
|
||||
for c in (data if isinstance(data, list) else [])
|
||||
} - {""})
|
||||
summary.append(f"{label}: classes={classes} titles={titles}")
|
||||
|
||||
machine.succeed(
|
||||
"cat > /tmp/float-class-summary.txt <<'EOF'\n"
|
||||
+ "\n".join(summary)
|
||||
+ "\nEOF"
|
||||
)
|
||||
print("\n".join(summary))
|
||||
# Copy into the test result via machine.copy_from_vm if available —
|
||||
# at minimum assert pinentry produced *some* client dump JSON.
|
||||
machine.succeed("test -s /tmp/float-class-summary.txt")
|
||||
'';
|
||||
}
|
||||
Reference in New Issue
Block a user