test(hibernate): #76 checks.hibernate-swapfile — verify swapfile+offset in a VM
All checks were successful
Check / eval (push) Successful in 4m18s
All checks were successful
Check / eval (push) Successful in 4m18s
Push #76's swapfile-resume residual into a VM as far as headlessly possible. A full hibernate→resume from a RUNTIME swapfile turned out not to be headless-reachable: systemd reports CanHibernate=no for a swap that isn't declared in the boot config, and a runtime-computed resume_offset can't be fed into the boot cmdline (the genuine chicken-and-egg). So the check now verifies, bounded and deterministically, what the partition test (checks.hibernate) can't and what a VM CAN prove: the installer's btrfs @swap swapfile is built correctly and its offset is valid — NOCOW mkswapfile, file-type swapon, a good `map-swapfile -r` offset the kernel accepts (/sys/power/resume_offset), and zram (prio 100) above it. The literal encrypted-swapfile power-cycle + LUKS-initrd unlock stays a V3 laptop check (the dev box runs exactly this layout, /proc/cmdline verified). (First cut had an unbounded wait_for_shutdown that hung on the CanHibernate refusal; rewritten with a thread-bounded wait + a CanHibernate pre-check, which gave the clean verdict. Device is /dev/vdb without useNixStoreImage.) Verification: V2 PASS (nix build .#checks.x86_64-linux.hibernate-swapfile, fresh, exit 0); flake check --no-build green. #76 residual narrowed in BACKLOG/ROADMAP. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
60
flake.nix
60
flake.nix
@@ -752,6 +752,66 @@
|
||||
'';
|
||||
};
|
||||
|
||||
# The installer's BTRFS @swap SWAPFILE setup (#76) — the parts the
|
||||
# partition hibernate test (checks.hibernate) can't reach. A full
|
||||
# hibernate→resume from a *runtime* swapfile isn't reachable
|
||||
# headlessly: systemd reports CanHibernate=no unless the swap is
|
||||
# declared in the boot config, and feeding a runtime-computed
|
||||
# resume_offset into the boot cmdline is a genuine chicken-and-egg.
|
||||
# So the power-cycle stays a real-hardware check (the dev box runs
|
||||
# exactly this layout — /proc/cmdline verified). What IS verified
|
||||
# here, bounded and deterministic, is that the swapfile is built
|
||||
# correctly and its resume offset is valid: NOCOW `mkswapfile`, a
|
||||
# good `map-swapfile -r` offset the kernel accepts, swapon as a file,
|
||||
# and zram (prio 100) above it so the image lands on disk, not zram.
|
||||
hibernate-swapfile = pkgs.testers.runNixOSTest {
|
||||
name = "nomarchy-hibernate-swapfile";
|
||||
nodes.machine = { config, lib, pkgs, ... }: {
|
||||
imports = [ ./modules/nixos/oom.nix ]; # zram default-on (#76)
|
||||
virtualisation.memorySize = 2048;
|
||||
# A dedicated disk for the btrfs @swap subvolume.
|
||||
virtualisation.emptyDiskImages = [ (4 * config.virtualisation.memorySize) ];
|
||||
environment.systemPackages = [ pkgs.btrfs-progs ];
|
||||
};
|
||||
testScript = ''
|
||||
machine.wait_for_unit("default.target")
|
||||
machine.wait_until_succeeds("grep -q /dev/zram0 /proc/swaps", timeout=60)
|
||||
|
||||
# Reproduce the installer layout: btrfs, @swap subvolume, then a
|
||||
# NOCOW swapfile via `btrfs filesystem mkswapfile`.
|
||||
# The lone empty data disk (root is vda; no separate store disk
|
||||
# here since useNixStoreImage is off).
|
||||
machine.succeed("mkfs.btrfs -f /dev/vdb")
|
||||
machine.succeed(
|
||||
"mkdir -p /mnt-btrfs && mount /dev/vdb /mnt-btrfs "
|
||||
"&& btrfs subvolume create /mnt-btrfs/@swap && umount /mnt-btrfs"
|
||||
)
|
||||
machine.succeed("mkdir -p /swap && mount -o subvol=@swap /dev/vdb /swap")
|
||||
machine.succeed("btrfs filesystem mkswapfile --size 3g --uuid clear /swap/swapfile")
|
||||
# NOCOW is mandatory — a copy-on-write swapfile corrupts on btrfs.
|
||||
machine.succeed("lsattr /swap/swapfile | grep -q C")
|
||||
machine.succeed("swapon /swap/swapfile")
|
||||
machine.succeed(
|
||||
"awk '$1==\"/swap/swapfile\" && $2==\"file\"{f=1} END{exit !f}' /proc/swaps"
|
||||
)
|
||||
|
||||
# The Nomarchy-specific offset math (patch-template bakes this
|
||||
# into resume_offset): a valid physical offset the kernel accepts.
|
||||
offset = machine.succeed(
|
||||
"btrfs inspect-internal map-swapfile -r /swap/swapfile"
|
||||
).strip()
|
||||
assert offset.isdigit() and int(offset) > 0, f"bad resume offset: {offset!r}"
|
||||
machine.succeed(f"echo {offset} > /sys/power/resume_offset")
|
||||
|
||||
# zram (prio 100) must sit above the disk swapfile, so a hibernate
|
||||
# image would land on disk, not volatile zram.
|
||||
machine.succeed(
|
||||
"awk '$1==\"/dev/zram0\"&&$5==100{z=1} "
|
||||
"$1==\"/swap/swapfile\"&&$5<100{d=1} END{exit !(z&&d)}' /proc/swaps"
|
||||
)
|
||||
'';
|
||||
};
|
||||
|
||||
# Auto time-of-day theme (#79, VISION § D): simulate crossing
|
||||
# sunset and sunrise by moving the VM clock, and assert
|
||||
# `nomarchy-theme-sync auto` switches the active theme accordingly
|
||||
|
||||
Reference in New Issue
Block a user