test(hibernate): #76 slice 3 — V2 hibernate→resume VM check (PASS)
All checks were successful
Check / eval (push) Successful in 4m11s
All checks were successful
Check / eval (push) Successful in 4m11s
Add checks.hibernate: a runNixOSTest modeled on nixpkgs' hibernate.nix, wired to the actual risk #76 introduces — coexistence with the zram swap that oom.nix now enables by default. It imports oom.nix, puts the disk swap at a lower priority than zram with an explicit boot.resumeDevice, and uses systemd-initrd. The test drives a full hibernate -> power-off -> resume -> crash cycle: a volatile ramfs marker survives resume but NOT a crash-boot (proving genuine resume), and it asserts zram sits at priority 100 while the disk swap is lower, so the hibernate image lands on disk rather than volatile zram. What QEMU genuinely can't do — feed a boot-time resume_offset for a btrfs swapfile (offset only exists after first-boot allocation) or drive a LUKS-initrd unlock — stays a V3 laptop check (HARDWARE-QUEUE, dev box already runs that exact layout), plus the no-swap notify in a real session. With this, all V0/V1/V2 work on #76 is done; #76 collapses to a [blocked:hw] stub and the design record moves to ROADMAP. Verification: V2 PASS — nix build .#checks.x86_64-linux.hibernate built the driver fresh and exited 0. nix flake check --no-build green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
68
flake.nix
68
flake.nix
@@ -684,6 +684,74 @@
|
||||
'';
|
||||
};
|
||||
|
||||
# Hibernate → resume (BACKLOG #76): the load-bearing risk the
|
||||
# hibernation default introduces is coexistence with the zram
|
||||
# swap that oom.nix now enables by default. Prove, in the VM
|
||||
# harness, that (a) a machine hibernates and resumes for real —
|
||||
# a volatile ramfs marker survives resume but NOT a fresh boot —
|
||||
# and (b) zram (priority 100) does not swallow the hibernate
|
||||
# image: the disk swap (lower priority, explicit resumeDevice)
|
||||
# carries it. Modeled on nixpkgs' hibernate.nix (partition swap +
|
||||
# systemd-initrd resume). What QEMU genuinely can't do is feed a
|
||||
# boot-time `resume_offset` for a btrfs *swapfile* (the offset
|
||||
# only exists after the file is allocated on first boot) or drive
|
||||
# a LUKS-initrd unlock end-to-end — so the encrypted @swap
|
||||
# swapfile + offset path stays a V3 laptop check (HARDWARE-QUEUE),
|
||||
# which already runs that exact layout. This test imports oom.nix
|
||||
# so it tracks the real zram default, not a hand-copy.
|
||||
hibernate = pkgs.testers.runNixOSTest {
|
||||
name = "nomarchy-hibernate";
|
||||
nodes.machine = { config, lib, pkgs, ... }: {
|
||||
imports = [ ./modules/nixos/oom.nix ]; # zram default-on (#76)
|
||||
virtualisation.memorySize = 2048;
|
||||
# Store + EFI must persist across the hibernate/resume/crash
|
||||
# boots; a disk swap ≥ 2×RAM holds the image.
|
||||
virtualisation.useNixStoreImage = true;
|
||||
virtualisation.useEFIBoot = true;
|
||||
virtualisation.emptyDiskImages = [ (2 * config.virtualisation.memorySize) ];
|
||||
boot.initrd.systemd.enable = true; # systemd-initrd, like Nomarchy
|
||||
# Disk swap at a LOWER priority than zram (100); explicit
|
||||
# resumeDevice — exactly how the installer wires hibernation —
|
||||
# so the image goes to disk regardless of zram's priority.
|
||||
swapDevices = lib.mkOverride 0 [
|
||||
{ device = "/dev/vdc"; options = [ "x-systemd.makefs" ]; priority = 1; }
|
||||
];
|
||||
boot.resumeDevice = "/dev/vdc";
|
||||
# Let the driver's shell reconnect after resume.
|
||||
systemd.services.backdoor.conflicts = [ "sleep.target" ];
|
||||
powerManagement.resumeCommands = "systemctl --no-block restart backdoor.service";
|
||||
};
|
||||
testScript = ''
|
||||
machine.wait_for_unit("default.target")
|
||||
|
||||
# zram (oom.nix) up at priority 100; disk swap present and lower
|
||||
# — so the hibernate image lands on disk, not volatile zram.
|
||||
machine.wait_until_succeeds("grep -q /dev/zram0 /proc/swaps", timeout=60)
|
||||
machine.succeed(
|
||||
"awk '$1==\"/dev/zram0\"&&$5==100{z=1} "
|
||||
"$1==\"/dev/vdc\"&&$5<100{d=1} END{exit !(z&&d)}' /proc/swaps"
|
||||
)
|
||||
|
||||
# Marker in volatile RAM: survives a resume, never a fresh boot.
|
||||
machine.succeed(
|
||||
"mkdir /run/test",
|
||||
"mount -t ramfs -o size=1m ramfs /run/test",
|
||||
"echo not persisted to disk > /run/test/suspended",
|
||||
)
|
||||
|
||||
# Hibernate, wait for real power-off, then resume.
|
||||
machine.execute("systemctl hibernate >&2 &", check_return=False)
|
||||
machine.wait_for_shutdown()
|
||||
machine.start()
|
||||
machine.succeed("grep 'not persisted to disk' /run/test/suspended")
|
||||
|
||||
# Prove it was a genuine resume: a crash-boot must lose the marker.
|
||||
machine.crash()
|
||||
machine.wait_for_unit("default.target")
|
||||
machine.fail("grep 'not persisted to disk' /run/test/suspended")
|
||||
'';
|
||||
};
|
||||
|
||||
# Snapshot GUI canary: btrfs-assistant crashes in libbtrfsutil's
|
||||
# UNPRIVILEGED subvolume iteration (btrfs-progs 6.17.1, fixed
|
||||
# upstream after 6.17.1) but runs fine as root — which is how the
|
||||
|
||||
Reference in New Issue
Block a user